手写经典双向循环链表

Posted stoneandatao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写经典双向循环链表相关的知识,希望对你有一定的参考价值。

问:写出双向循环链表,并写出增、删、查

思路:一个链表要知道从哪里开始,所以要有头,还要知道有多大,所以要有size。链表的每一个疙瘩,我们叫它节点(node),它有其本身的值,还存着上一个节点和下一个节点的引用,我们要把这些节点链起来,正着链一周,反着链一周。

技术图片

 1   private Node head;
 2     private int size = 0;
 3     private class Node
 4         Node(E e) 
 5             data = e;
 6         
 7         E data;
 8         Node next;
 9         Node prev;
10     

 

①咱先写往尾部追加元素

 

技术图片

 

    //追加元素
    public boolean add(E e) 
        //如果什么都没有
        if(head == null) 
            //把新加进来的节点作为头节点
            head = new Node(e);
            //头节点的下一个是其本身
            head.next = head;
            //头节点的上一个是其本身
            head.prev = head;
        //如果刚开始有节点
        else 
            Node node = new Node(e);
            //先找到最后一个节点,它就是头节点的上一个节点
            //注意:这是经典的双向循环链表,jdk8后最后一个节点是拿出来作为属性的
            Node last = head.prev;
            //把新加进来的元素链到末尾
            head.prev = node;
            node.prev = last;
            last.next = node;
            node.next = head;
        
        size++;
        return true;
    

 

②根据下标找对应的元素,当然,下标也是从0开始的

技术图片

    //根据下标获取元素
    public E get(int index) 
        if(index < 0 || index > size) throw new IndexOutOfBoundsException("下标越界");
        
        Node node;
        node = find(index);
        return node.data;
            
        

    //根据下标获取节点
    private Node find(int index) 
        Node node;
        //如果下标小于size的一半,则从头开始找
        if(index < size>>1) 
            node = head;
            for(int i = 0; i < index; i++) 
                node = node.next;
            
        
        else 
        //如果下标大于size的一半,则从最后一个开始往前找
        //其实一直是往后找也行,就是太笨了
            node = head.prev;
            for(int i = size - 1; i > index; i--) 
                node = node.prev;
            
        
        return node;
    

 

③add()的重载方法,根据下标和给定的元素插入节点

技术图片

    //根据下标插入元素e
    public boolean add(int index, E e) 
        
        if(index < 0 || index > size) throw new IndexOutOfBoundsException("下标越界");
        //如果下标等于size,那就是追加元素,调用add方法
        if(index == size) return add(e);
        //先根据下标找到元素,它就是即将要插入的节点的下一个节点
        Node next = find(index);
        //再找到上一个节点
        Node prev = next.prev;
        //准备好新节点
        Node node = new Node(e);
        //把新节点链进来,顺时针走一波,逆时针走一波
        prev.next = node;
        node.next = next;
        next.prev = node;
        node.prev = prev;
        //如果是在头部插入,再多做一个工作,就是把插入的节点作为新的头
        if(index == 0) head = node;
        
        size++;
        
        return true;
    

 

④根据下标删元素

 

技术图片

//根据下标删元素
    public E remove(int index) 
        
        if(index < 0 || index >= size) throw new IndexOutOfBoundsException("下标越界");
        //如果就剩个头
        if(size == 1) 
            //取出头节点的值
            E e = head.data;
            //头节点被gc回收掉
            head = null;
            size--;
            //返回取出来的值
            return e;
        
        //先根据下标找到要删的节点
        Node node = find(index);
        Node prev = node.prev;
        Node next = node.next;
        //把要删的节点晾在一边
        next.prev = prev;
        prev.next = next;
        //下面可写可不写,我们写明显一点,让被删的节点回收掉
        node.next = null;
        node.prev = null;
        //你要是删头节点的话,把下一个节点当做新的头
        if(index == 0) head = next;
        
        size--;
        
        return node.data;
    
    

 

 

完整代码:

  1 //双向循环链表
  2 public class LinkedList<E> 
  3     private Node head;
  4     private int size = 0;
  5     private class Node
  6         Node(E e) 
  7             data = e;
  8         
  9         E data;
 10         Node next;
 11         Node prev;
 12     
 13     //返回链表大小
 14     public int size() 
 15         return size;
 16     
 17     //toString()方法
 18     public String toString() 
 19         if(head == null) 
 20             return "[]";
 21         
 22         StringBuilder buf = new StringBuilder("[");
 23         buf.append(head.data);
 24         
 25         Node node = head.next;
 26         //咱这用的是node和head的关系,当然你也可以用index和size的关系来遍历链表
 27         while(node != head) 
 28             //这里一个逗号一个数据是成对出现的
 29             buf.append(", ").append(node.data);
 30             node = node.next;
 31         
 32         buf.append("]");
 33         return buf.toString();
 34     
 35     //根据下标删元素
 36     public E remove(int index) 
 37         
 38         if(index < 0 || index >= size) throw new IndexOutOfBoundsException("下标越界");
 39         //如果就剩个头
 40         if(size == 1) 
 41             //取出头节点的值
 42             E e = head.data;
 43             //头节点被gc回收掉
 44             head = null;
 45             size--;
 46             //返回取出来的值
 47             return e;
 48         
 49         //先根据下标找到要删的节点
 50         Node node = find(index);
 51         Node prev = node.prev;
 52         Node next = node.next;
 53         //把要删的节点晾在一边
 54         next.prev = prev;
 55         prev.next = next;
 56         //下面可写可不写,我们写明显一点,让被删的节点回收掉
 57         node.next = null;
 58         node.prev = null;
 59         //你要是删头节点的话,把下一个节点当做新的头
 60         if(index == 0) head = next;
 61         
 62         size--;
 63         
 64         return node.data;
 65     
 66     
 67     //根据下标插入元素e
 68     public boolean add(int index, E e) 
 69         
 70         if(index < 0 || index > size) throw new IndexOutOfBoundsException("下标越界");
 71         //如果下标等于size,那就是追加元素,调用add方法
 72         if(index == size) return add(e);
 73         //先根据下标找到元素,它就是即将要插入的节点的下一个节点
 74         Node next = find(index);
 75         //再找到上一个节点
 76         Node prev = next.prev;
 77         //准备好新节点
 78         Node node = new Node(e);
 79         //把新节点链进来,顺时针走一波,逆时针走一波
 80         prev.next = node;
 81         node.next = next;
 82         next.prev = node;
 83         node.prev = prev;
 84         //如果是在头部插入,再多做一个工作,就是把插入的节点作为新的头
 85         if(index == 0) head = node;
 86         
 87         size++;
 88         
 89         return true;
 90     
 91     
 92     //追加元素
 93     public boolean add(E e) 
 94         //如果什么都没有
 95         if(head == null) 
 96             //把新加进来的节点作为头节点
 97             head = new Node(e);
 98             //头节点的下一个是其本身
 99             head.next = head;
100             //头节点的上一个是其本身
101             head.prev = head;
102         //如果刚开始有节点
103         else 
104             Node node = new Node(e);
105             //先找到最后一个节点,它就是头节点的上一个节点
106             //注意:这是经典的双向循环链表,jdk8后最后一个节点是拿出来作为属性的
107             Node last = head.prev;
108             //把新加进来的元素链到末尾
109             head.prev = node;
110             node.prev = last;
111             last.next = node;
112             node.next = head;
113         
114         size++;
115         return true;
116     
117     
118     //根据下标获取元素
119     public E get(int index) 
120         if(index < 0 || index > size) throw new IndexOutOfBoundsException("下标越界");
121         
122         Node node;
123         node = find(index);
124         return node.data;
125             
126         
127 
128     //根据下标获取节点
129     private Node find(int index) 
130         Node node;
131         //如果下标小于size的一半,则从头开始找
132         if(index < size>>1) 
133             node = head;
134             for(int i = 0; i < index; i++) 
135                 node = node.next;
136             
137         
138         else 
139         //如果下标大于size的一半,则从最后一个开始往前找
140         //其实一直是往后找也行,就是太笨了
141             node = head.prev;
142             for(int i = size - 1; i > index; i--) 
143                 node = node.prev;
144             
145         
146         return node;
147     
148         
149 

 

以上是关于手写经典双向循环链表的主要内容,如果未能解决你的问题,请参考以下文章

静态链表循环链表双向链表

单链表循环链表双向链表的比较

数据结构05——静态链表循环链表双向链表

静态链表循环链表双向链表(C代码实现)

数据结构与算法笔记—— 链表(单链表循环链表双向链表)

[读书笔记]-大话数据结构-3-线性表-静态链表循环链表和双向链表