自己動手擼一個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