【数据结构与算法】模拟实现LinkedList类

简介: Java LinkedList(链表)类似于 ArrayList,是一种常用的数据容器。 与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。

LinkedList简介


Java LinkedList(链表)类似于 ArrayList,是一种常用的数据容器。 与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。

LinkedList底层是一个双向链表,如下:

10.png

一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接

双向链表有头节点和尾结点。

因此要实现双向链表,我们就要用类将双向链表的各个部分给抽取出来

  static class ListNode{

       public int val;//数据域

       public ListNode prev;//保存前一个节点的地址

       public ListNode next;//保存后一个结点的地址

       public ListNode(int val) {

           this.val = val;

       }

   }

   public ListNode head;//头节点

   public ListNode tail;//尾结点


头插法创建链表


要用头插法建立双向链表,与单链表不同,双向链表的每一个结点都有三个域,因此我们要想清楚改哪个地方,看下图:

11.png

由图我我们可知,我们要改变插入结点的next域,要改变头节点的prev域,然后将插入结点的prev置为null,最后让头节点指向插入的结点,这样在头结点插入一个结点就完成了。

但这个只是一般情况下插入,还有一种特殊情况,就是遇到空链表。

如果是空链表的情况,那么头节点就是要插入的这个结点,尾节点也是要插入的这个结点。

  public void addFirst(int data){

       ListNode node = new ListNode(data);

       //空链表

       if(this.head == null){

           this.head = node;

           this.tail = node;

       }else {

           node.next = this.head;

           this.head.prev = node;

           this.head = node;

       }

   }


尾插法创建链表


13.png

看上图,我们可知用尾插法插入一个结点,要改变尾结点的next域,将要插入的next域置为空,将插入结点的prev域指向尾结点,再将尾节点指向要插入的结点就可以了。

同时也不要忘记考虑空链表的情况,与头插法相同,如果为空,那么要插入的这个结点就既是头结点也是尾结点。

  public void addLast(int data){

       ListNode node = new ListNode(data);

       //空链表

       if(this.head == null){

           this.head = node;

           this.tail = node;

       }else {

           this.tail.next = node;

           node.prev = this.tail;

           this.tail = node;

       }

   }


任意位置插入,第一个数据节点为0号下标


在插入数据时,我们首先要对要插入的位置index进行判断,判断index是否合法,index 位置合法才能插入数据。其次我们要考虑index在特殊位置,例如:在头节点和尾结点进行插入数据。

  /**

    * 任意位置插入,第一个数据节点为0号下标

    * @param index

    * 要对index位置判断是否合法

    * index在特殊位置 -头插和尾插

    * @param data

    * @return

    */

   public boolean addIndex(int index,int data){

       if(index<0 || size()<index){

           System.out.println("index位置不合法");

           return false;

       }

       if(index == 0){

           addFirst(data);

           return true;

       }

       if(index == size()){

           addLast(data);

           return true;

       }

       ListNode node = new ListNode(data);

       ListNode cur = findIndexListNode(index);

       node.next = cur;

       cur.prev.next = node;

       node.prev = cur.prev;

       cur.prev = node;

       return false;

   }

   public ListNode findIndexListNode(int index){

       ListNode cur = this.head;

       while(index != 0){

           cur = cur.next;

           index--;

       }

       return cur;

   }


查找是否包含关键字key是否在链表当中


查找是否包含关键字key,也就是遍历链表,与各个节点的数据域进行比较,如果相等就返回true,如果不相等,就返回false。

   //查找是否包含关键字key是否在单链表当中

   public boolean contains(int key){

       ListNode cur = this.head;

       while(cur != null){

           if(cur.val == key){

               return true;

           }

           cur= cur.next;

       }

       return false;

   }


删除第一次出现关键字为key的节点


删除双向链表的节点,首先要明确我们要改那些值,看下图:

14.png

假设删除的是值为3的结点,那么此时要改变两个地方的值,就是删除结点前一个结点的next,以及后一个结点的prev.改完之后,就可以让值为2的结点直接找到值为4的结点,值为4的节点也可以直接找到值为2的结点.

然后我们还要考虑到删除的结点在特殊的位置,有两种情况:1.删除的结点在头节点或者是尾结点 2.只有一个结点

   /**

    * 删除第一次出现关键字为key的节点

    * 可能会遇到的特殊情况

    *    1.删除的节点在头或者尾

    *    2.只有一个结点

    * @param key

    */

   public void remove(int key){

       ListNode cur = this.head;

       while(cur != null){

           if(cur.val == key){

               if(cur==head){//头节点

                   if(cur.next==null){//只有一个结点

                       this.head = null;

                       this.tail = null;

                       return;

                   }

                   this.head = head.next;

                   this.head.prev = null;

               }else{

                   cur.prev.next = cur.next;

                   if(cur.next != null){

                       cur.next.prev = cur.prev;

                   }else{//删除的是尾结点的情况

                       this.tail = cur.prev;

                   }

               }

               return;

           }else{

               cur = cur.next;

           }

       }

   }


删除所有值为key的节点


删除所有为key的结点,只需要在前面删除一个为key的结点代码上,把return删了就可以了.

  //删除所有值为key的节点

   public void removeAllKey(int key){

       ListNode cur = this.head;

       while(cur != null){

           if(cur.val == key){

               if(cur==head){

                   if(cur.next==null){

                       this.head = null;

                       this.tail = null;

                       return;

                   }

                   this.head = head.next;

                   this.head.prev = null;

               }else{

                   cur.prev.next = cur.next;

                   if(cur.next != null){

                       cur.next.prev = cur.prev;

                   }else{

                       this.tail = cur.prev;

                   }

               }

           }

           cur = cur.next;

       }

   }


得到链表的长度


得到链表的长度,实际上就相当于遍历了一边链表,如果当前节点不为空count就+1,最后return count就可以了。

  //得到单链表的长度

   public int size(){

       ListNode cur = head;

       int count = 0;

       while(cur != null){

           count++;

           cur = cur.next;

       }

       return count;

   }


打印链表


打印双向链表,我们可以从前往后进行打印,也可以从后往前打印。一般打印链表都是从前往后打印。具体代码实现如下:

  public void display(){

       ListNode cur = head;

       while(cur != null){

           System.out.print(cur.val+" ");

           cur = cur.next;

       }

   }


清空链表


清空双向链表,不能直接将头节点和尾节点置为null

因为这是双向链表,直接置为null也可能会有别的结点引用,所以双向链表只能一个一个置空

   public void clear(){

       //error

       /*this.head = null;

       this.tail = null;*/

       ListNode cur= this.head;

       while(cur != null){

           ListNode curNext = cur.next;

           cur.prev = null;

           cur.next = null;

           cur = curNext;

       }

       this.head = null;

       this.tail = null;

   }


完整代码:


import java.util.List;

public class MyLinkedList {

   static class ListNode{

       public int val;

       public ListNode prev;

       public ListNode next;

       public ListNode(int val) {

           this.val = val;

       }

   }

   public ListNode head;

   public ListNode tail;

   /**

    * 无头双向链表实现

    * @param data

    */

   //头插法

   public void addFirst(int data){

       ListNode node = new ListNode(data);

       //空链表

       if(this.head == null){

           this.head = node;

           this.tail = node;

       }else {

           node.next = this.head;

           this.head.prev = node;

           this.head = node;

       }

   }

   //尾插法

   public void addLast(int data){

       ListNode node = new ListNode(data);

       //空链表

       if(this.head == null){

           this.head = node;

           this.tail = node;

       }else {

           this.tail.next = node;

           node.prev = this.tail;

           this.tail = node;

       }

   }

   /**

    * 任意位置插入,第一个数据节点为0号下标

    * @param index

    * 要对index位置判断是否合法

    * index在特殊位置 -头插和尾插

    * @param data

    * @return

    */

   public boolean addIndex(int index,int data){

       if(index<0 || size()<index){

           System.out.println("index位置不合法");

           return false;

       }

       if(index == 0){

           addFirst(data);

           return true;

       }

       if(index == size()){

           addLast(data);

           return true;

       }

       ListNode node = new ListNode(data);

       ListNode cur = findIndexListNode(index);

       node.next = cur;

       cur.prev.next = node;

       node.prev = cur.prev;

       cur.prev = node;

       return false;

   }

   public ListNode findIndexListNode(int index){

       ListNode cur = this.head;

       while(index != 0){

           cur = cur.next;

           index--;

       }

       return cur;

   }

   //查找是否包含关键字key是否在单链表当中

   public boolean contains(int key){

       ListNode cur = this.head;

       while(cur != null){

           if(cur.val == key){

               return true;

           }

           cur= cur.next;

       }

       return false;

   }

   /**

    * 删除第一次出现关键字为key的节点

    * 可能会遇到的特殊情况

    *    1.删除的节点在头或者尾

    *    2.只有一个结点

    * @param key

    */

   public void remove(int key){

       ListNode cur = this.head;

       while(cur != null){

           if(cur.val == key){

               if(cur==head){

                   if(cur.next==null){

                       this.head = null;

                       this.tail = null;

                       return;

                   }

                   this.head = head.next;

                   this.head.prev = null;

               }else{

                   cur.prev.next = cur.next;

                   if(cur.next != null){

                       cur.next.prev = cur.prev;

                   }else{

                       this.tail = cur.prev;

                   }

               }

               return;

           }else{

               cur = cur.next;

           }

       }

   }

   //删除所有值为key的节点

   public void removeAllKey(int key){

       ListNode cur = this.head;

       while(cur != null){

           if(cur.val == key){

               if(cur==head){

                   if(cur.next==null){

                       this.head = null;

                       this.tail = null;

                       return;

                   }

                   this.head = head.next;

                   this.head.prev = null;

               }else{

                   cur.prev.next = cur.next;

                   if(cur.next != null){

                       cur.next.prev = cur.prev;

                   }else{

                       this.tail = cur.prev;

                   }

               }

           }

           cur = cur.next;

       }

   }

   //得到单链表的长度

   public int size(){

       ListNode cur = head;

       int count = 0;

       while(cur != null){

           count++;

           cur = cur.next;

       }

       return count;

   }

   public void display(){

       ListNode cur = head;

       while(cur != null){

           System.out.print(cur.val+" ");

           cur = cur.next;

       }

   }

   public void clear(){

       //error

       this.head = null;

       this.tail = null;

      /* ListNode cur= this.head;

       while(cur != null){

           ListNode curNext = cur.next;

           cur.prev = null;

           cur.next = null;

           cur = curNext;

       }

       this.head = null;

       this.tail = null;*/

   }

}


总结:


LinkedList作为Java中的一个重要集合,底层是个双向链表,我只是模拟实现了一些简单的功能,大家如果要研究LinkedList底层到底是怎么实现的,大家开始去看源码比较好.

相关文章
|
19天前
|
机器学习/深度学习 算法
【优选算法】—— 滑动窗口类问题
【优选算法】—— 滑动窗口类问题
|
19天前
|
机器学习/深度学习 算法
【数学建模竞赛】评价类赛题常用算法解析
【数学建模竞赛】评价类赛题常用算法解析
52 0
|
12天前
|
算法 测试技术
【数据结构与算法 | 基础篇】模拟LinkedList实现的双向循环链表
【数据结构与算法 | 基础篇】模拟LinkedList实现的双向循环链表
|
12天前
|
存储 算法 Java
【数据结构与算法 | 基础篇】模拟LinkedList实现的双向链表
【数据结构与算法 | 基础篇】模拟LinkedList实现的双向链表
|
12天前
|
算法
【数据结构与算法 | 基础篇】模拟LinkedList实现的链表(无哨兵)
【数据结构与算法 | 基础篇】模拟LinkedList实现的链表(无哨兵)
|
19天前
|
机器学习/深度学习 监控 算法
【数学建模竞赛】优化类赛题常用算法解析
【数学建模竞赛】优化类赛题常用算法解析
67 2
|
19天前
|
机器学习/深度学习 算法 vr&ar
【数学建模竞赛】预测类赛题常用算法解析
【数学建模竞赛】预测类赛题常用算法解析
73 0
|
19天前
|
算法 Java 索引
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
36 0
|
19天前
|
存储 算法 Java
【数据结构与算法】2、链表(简单模拟 Java 中的 LinkedList 集合,反转链表面试题)
【数据结构与算法】2、链表(简单模拟 Java 中的 LinkedList 集合,反转链表面试题)
44 0
|
19天前
|
存储 算法 Java
ArrayList vs. LinkedList:数据结构之争
ArrayList vs. LinkedList:数据结构之争
25 0