1. 程式人生 > >自己動手擼一個LinkedList

自己動手擼一個LinkedList

自己動手擼一個LinkedList

1. 原理

LinkedList是基於雙鏈表的動態陣列,資料新增刪除效率高,只需要改變指標指向即可,但是訪問資料的平均效率低,需要對連結串列進行遍歷。因此,LinkedList善於進行一些插入、刪除操作,不利於進行檢索操作。LinkedList和ArrayList這兩個list在我們程式碼裡會經常用到,因此,小編自定義實現LinkedList的簡易版--MyLinkedList。

2. public API

void clear()                     --> 置空
boolean isEmpty()                --> 判空
int size()                       --> 返回連結串列的長度
AnyType get(int idx)             --> 根據索引檢索元素
AnyType set(int idx)             --> 根據索引跟新元素
boolean add(AnyType x)           --> 新增元素x
boolean add(AnyType x,int idx)  --> 根據索引新增元素x
AnyType remove(int idx)          --> 根據索引刪除元素x
String toString()                --> 列印連結串列

3. 圖解核心操作

  • 一個節點Node類的資料結構

  • doClear( ) 方法,初始化一個雙鏈表,先定義一個頭結點beginMarker,然後定義一個尾結點endMarker,前驅指向頭結點,最後頭結點的後繼指向尾結點。

  • 新增元素,先定義一個被新增元素x的節點,使它前驅指向被插入位置的前一個,後繼指向被插入位置的節點,這是第一步,然後將被插入的前一個節點的next指向此節點,被插入位置的節點的prev指向此節點。

    Node<AnyType> newNode = new Node<>(x, p.prev, p);       // ①②
    newNode.prev.next = newNode; // ③
    p.prev = newNode; // ④

    當然,第三步和第四步可以合併:

    Node<AnyType> newNode = new Node<>(x, p.prev, p);       // ①②
    p.prev = p.prev.next = newNode; // ③④

    沒想到以上4步全可以合併為:

    p.prev = p.prev.next = new Node<>(x, p.prev, p);        // ①②③④

    精闢!

  • 刪除元素,根據索引找到對應的節點p,將p的後繼的prev指向p的前驅,將p的前驅的next指向p的後繼。

    p.next.prev = p.prev;
    p.prev.next = p.next;
  • 檢索節點getNode,LinkedList可以很快很方便地插入和刪除元素,但是對於檢索元素則就慢了,我們可以將索引分為前半部分和後半部分,如果索引在前半部分,我們就向後的方向遍歷該連結串列;同樣的道理,如果索引在後半部分,我們就從終端開始往回走,向前遍歷該連結串列,這樣可以提高一下檢索速度吧。

    // 從頭結點開始向後找
    if (idx < size() / 2) {
    p = beginMarker.next;
    for (int i = 0; i < idx; i++) {
    p = p.next;
    }
    }
    // 從尾節點開始向前找
    else {
    p = endMarker;
       for (int i = size(); i > idx; i--) {
      p = p.prev;
      }
    }

4. MyLinkedList程式碼實現

  1 package com.hx.list;
  2 
  3 /**
  4  * @author: wenhx
  5  * @date: Created in 2019/10/17 16:11
  6  * @description: 用雙鏈表實現MyLinkedList
  7  */
  8 public class MyLinkedList<AnyType> implements Iterable<AnyType> {
  9 
 10 
 11     private int theSize;
 12     private int modCount = 0;
 13     private Node<AnyType> beginMarker;
 14     private Node<AnyType> endMarker;
 15 
 16     /**
 17      * 內部類,定義連結串列的節點
 18      */
 19     private static class Node<AnyType> {
 20 
 21         public AnyType data;
 22         public Node<AnyType> prev;
 23         public Node<AnyType> next;
 24 
 25         public Node(AnyType d, Node<AnyType> p, Node<AnyType> n) {
 26             data = d;
 27             prev = p;
 28             next = n;
 29         }
 30     }
 31 
 32     /**
 33      * 構造器
 34      */
 35     public MyLinkedList() {
 36         doClear();
 37     }
 38 
 39     /**
 40      * 判空
 41      */
 42     public boolean isEmpty() {
 43         return size() == 0;
 44     }
 45 
 46     /**
 47      * 清空
 48      */
 49     public void clear() {
 50         doClear();
 51     }
 52 
 53 
 54     /**
 55      * 返回連結串列的長度
 56      */
 57     public int size() {
 58         return theSize;
 59     }
 60 
 61     /**
 62      * 根據索引檢索元素
 63      */
 64     public AnyType get(int idx) {
 65         return getNode(idx).data;
 66     }
 67 
 68     /**
 69      * 根據索引跟新元素
 70      */
 71     public AnyType set(int idx, AnyType newVal) {
 72         Node<AnyType> p = getNode(idx);
 73         AnyType oldVal = p.data;
 74         p.data = newVal;
 75         return oldVal;
 76     }
 77 
 78     /**
 79      * 新增元素x
 80      */
 81     public boolean add(AnyType x) {
 82         add(size(), x);
 83         return true;
 84     }
 85 
 86     /**
 87      * 根據索引新增元素
 88      */
 89     public void add(int idx, AnyType x) {
 90         addBefore(getNode(idx, 0, size()), x);
 91     }
 92 
 93     /**
 94      * 根據索引刪除元素
 95      */
 96     public AnyType remove(int idx) {
 97         return remove(getNode(idx));
 98     }
 99 
100     /**
101      * 列印連結串列
102      */
103     public String toString() {
104         StringBuilder sb = new StringBuilder("[ ");
105 
106         for (AnyType x : this) {
107             sb.append(x + " ");
108         }
109         sb.append("]");
110 
111         return new String(sb);
112     }
113 
114     /**
115      * 清空連結串列(實現)
116      */
117     private void doClear() {
118         beginMarker = new Node<>(null, null, null);
119         endMarker = new Node<>(null, beginMarker, null);
120         beginMarker.next = endMarker;
121         theSize = 0;
122         modCount++;
123     }
124 
125     /**
126      * 根據索引檢索節點
127      */
128     private Node<AnyType> getNode(int idx) {
129         return getNode(idx, 0, size() - 1);
130     }
131 
132     /**
133      * 檢索節點
134      */
135     private Node<AnyType> getNode(int idx, int lower, int upper) {
136         Node<AnyType> p;
137 
138         if (idx < lower || idx > upper) {
139             throw new IndexOutOfBoundsException("getNode index: " + idx + "; size: " + size());
140         }
141 
142         if (idx < size() / 2) {
143             p = beginMarker.next;
144             for (int i = 0; i < idx; i++) {
145                 p = p.next;
146             }
147         } else {
148             p = endMarker;
149             for (int i = size(); i > idx; i--) {
150                 p = p.prev;
151             }
152         }
153 
154         return p;
155     }
156 
157     /**
158      * 插入節點
159      */
160     private void addBefore(Node<AnyType> p, AnyType x) {
161         Node<AnyType> newNode = new Node<>(x, p.prev, p);
162         newNode.prev.next = newNode;
163         p.prev = newNode;
164         theSize++;
165         modCount++;
166     }
167 
168     /**
169      * 刪除節點p
170      */
171     private AnyType remove(Node<AnyType> p) {
172         p.next.prev = p.prev;
173         p.prev.next = p.next;
174         theSize--;
175         modCount++;
176 
177         return p.data;
178     }
179 
180 
181     /**
182      * 返回一個迭代器物件,用於遍歷連結串列
183      */
184     public java.util.Iterator<AnyType> iterator() {
185         return new LinkedListIterator();
186     }
187 
188     /**
189      * LinkedListIterator迭代器的實現
190      */
191     private class LinkedListIterator implements java.util.Iterator<AnyType> {
192 
193         private Node<AnyType> current = beginMarker.next;
194         private int expectedModCount = modCount;
195         private boolean okToRemove = false;
196 
197         public boolean hasNext() {
198             return current != endMarker;
199         }
200 
201         public AnyType next() {
202             if (modCount != expectedModCount) {
203                 throw new java.util.ConcurrentModificationException();
204             }
205             if (!hasNext()) {
206                 throw new java.util.NoSuchElementException();
207             }
208 
209             AnyType nextItem = current.data;
210             current = current.next;
211             okToRemove = true;
212             return nextItem;
213         }
214 
215         public void remove() {
216             if (modCount != expectedModCount) {
217                 throw new java.util.ConcurrentModificationException();
218             }
219             if (!okToRemove) {
220                 throw new IllegalStateException();
221             }
222 
223             MyLinkedList.this.remove(current.prev);
224             expectedModCount++;
225             okToRemove = false;
226         }
227     }
228 
229 
230     /**
231      * 主方法:用來測試MyLinkedList
232      */
233     public static void main(String[] args) {
234         MyLinkedList<Integer> myLinkedList = new MyLinkedList<>();
235 
236         for (int i = 0; i < 10; i++) {
237             myLinkedList.add(i);
238         }
239         for (int i = 20; i < 30; i++) {
240             myLinkedList.add(0, i);
241         }
242 
243         System.out.println(myLinkedList.toString());
244         System.out.println("----------");
245         myLinkedList.remove(0);
246         myLinkedList.remove(myLinkedList.size() - 1);
247         System.out.println(myLinkedList);
248         System.out.println("----------");
249         java.util.Iterator<Integer> itr = myLinkedList.iterator();
250         while (itr.hasNext()) {
251             itr.next();
252             itr.remove();
253             System.out.println(myLinkedList);
254         }
255     }
256 }

 

完成,撒花,一個迷你版的LinkedList就寫好啦,下次有空再寫一個迷你版的ArrayList...

後記:

若有不當之處,可向小編反饋,一起交流學習,共同進步。

個人部落格地址:https://www.cnblogs.com/q964024886/

GitHub地址:https://github.com/wenhaixiong