【從蛋殼到滿天飛】JAVA 數據結構解析和算法實現,所有文章大概的內容以下:
Arrays(數組)、Stacks(棧)、Queues(隊列)、LinkedList(鏈表)、Recursion(遞歸思想)、BinarySearchTree(二分搜索樹)、Set(集合)、Map(映射)、Heap(堆)、PriorityQueue(優先隊列)、SegmentTree(線段樹)、Trie(字典樹)、UnionFind(並查集)、AVLTree(AVL 平衡樹)、RedBlackTree(紅黑平衡樹)、HashTable(哈希表)html
源代碼有三個:ES6(單個單個的 class 類型的 js 文件) | JS + HTML(一個 js 配合一個 html)| JAVA (一個一個的工程)java
所有源代碼已上傳 github,點擊我吧,光看文章可以掌握兩成,動手敲代碼、動腦思考、畫圖才能夠掌握八成。node
本文章適合 對數據結構想了解而且感興趣的人羣,文章風格一如既往如此,就以爲手機上看起來比較方便,這樣顯得比較有條理,整理這些筆記加源碼,時間跨度也算將近半年時間了,但願對想學習數據結構的人或者正在學習數據結構的人羣有幫助。git
鏈表是很是重要的線性數據結構github
鏈表是真正的動態數據結構面試
對於鏈表來講它涉及到了計算機領域一個很是重要的概念算法
鏈表自己也是有它很是清晰的遞歸結構的,數組
鏈表這種數據結構自己就具備功能性數據結構
數據存儲在「節點」(Node)中app
class Node { E e; Node next; }
鏈表的優勢
對於鏈表來講,你須要多少個數據。
鏈表的缺點
O(1)
的複雜度取出這個元素,數組
scores[2]
鏈表
對比
要清楚何時使用數組這樣的靜態數據結構,
簡單的代碼示例MyLinkedList
public class MyLinkedList<E> { // 隱藏內部實現,不須要讓用戶知道 private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } }
鏈表是經過節點來裝載元素
MyLinkedList
中給自定義數組添加元素是從數組尾部開始添加,
添加操做原理
node.next = head
,head = node
,在鏈表頭部添加元素很是簡單,
在鏈表中間添加元素,
node.next = prev.next
,prev.next = node
,在鏈表的操做中不少時候順序很是重要,
node.next = prev.next
和prev.next = node
prev.next = node
在前,node.next = prev.next
在後,這樣一來邏輯就不成立了,在鏈表的 index(0-based)位置添加元素 e
若是剛接觸鏈表,對鏈表不熟悉,
(class: MyLinkedList)
MyLinkedList
public class MyLinkedList<E> { // 隱藏內部實現,不須要讓用戶知道 private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } private Node head; private int size; public MyLinkedList () { head = null; size = 0; } // ... // 其它的構造函數,例如傳進來一個數組,將數組轉換爲鏈表 // 獲取鏈表中元素的個數 public int getSize () { return size; } // 返回當前鏈表是否爲空 public boolean isEmpty () { return size == 0; } // 在鏈表頭部添加一個元素 e public void addFirst (E e) { // 寫法一 // Node node = new Node(e, head); // head = node; // 寫法二 // Node node = new Node(e); // node.next = head; // head = node; // 寫法三 head = new Node(e, head); size ++; } // 在鏈表指定索引出插入一個元素 public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("add error. index < 0 or index > size"); } if (index == 0) { addFirst(e); }else { // 第一個prev就是head Node prev = head; // 不斷的搜索 一直經過next來進行檢索 for (int i = 0; i < index - 1 ; i++) { prev = prev.next; } // 第一種方式 // Node node = new Node(e); // node.next = prev.next; // prev.next = node; // 第二種方式 prev.next = new Node(e, prev.next); size ++; } } // 在鏈表尾部添加一個元素 public void addLast (E e) { insert(size, e); } }
在鏈表中進行指定索引處插入元素時
node.next = prev.next
和prev.next = node
了,node.next = head
和head = node
,爲何對鏈表頭插入元素那麼特殊?
有了 dummyHead 以後就不須要處理頭節點這個特殊的操做
node.next = dummyHead.next
、dummyHead.next = node
,鏈表操做的實際原理
node.next = head.next;head = node;
。node.next = dummyHead.next; dummyHead.next = node;
,dummyHead.next
纔是鏈表中第一個實際記錄的節點,(class: MyLinkedList)
MyLinkedList
public class MyLinkedList<E> { // 隱藏內部實現,不須要讓用戶知道 private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } private Node dummyHead; private int size; public MyLinkedList () { dummyHead = new Node(null, null); size = 0; } // ... // 其它的構造函數,例如傳進來一個數組,將數組轉換爲鏈表 // 獲取鏈表中元素的個數 public int getSize () { return size; } // 返回當前鏈表是否爲空 public boolean isEmpty () { return size == 0; } // 在鏈表頭部添加一個元素 e public void addFirst (E e) { // 寫法一 // Node node = new Node(e, head); // head = node; // 寫法二 // Node node = new Node(e); // node.next = dummyHead.next; // dummyHead.next = node; // 寫法三 // dummyHead.next = new Node(e, dummyHead.next); // size ++; // 寫法四 insert(0, e); } // 在鏈表指定索引出插入一個元素 public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("add error. index < 0 or index > size"); } // 第一個prev就是dummyHead Node prev = dummyHead; // 不斷的搜索 一直經過next來進行檢索 for (int i = 0; i < index ; i++) { prev = prev.next; } // 第一種方式 // Node node = new Node(e); // node.next = prev.next; // prev.next = node; // 第二種方式 prev.next = new Node(e, prev.next); size ++; } // 在鏈表尾部添加一個元素 public void addLast (E e) { insert(size, e); } }
若是要找指定索引元素的前一個節點
dummyHead
開始遍歷,dummyHead.next
開始遍歷。(class: MyLinkedList, class: Main)
MyLinkedList
public class MyLinkedList<E> { // 隱藏內部實現,不須要讓用戶知道 private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } private Node dummyHead; private int size; public MyLinkedList () { dummyHead = new Node(null, null); size = 0; } // ... // 其它的構造函數,例如傳進來一個數組,將數組轉換爲鏈表 // 獲取鏈表中元素的個數 public int getSize () { return size; } // 返回當前鏈表是否爲空 public boolean isEmpty () { return size == 0; } // 在鏈表頭部添加一個元素 e public void addFirst (E e) { // 寫法一 // Node node = new Node(e, head); // head = node; // 寫法二 // Node node = new Node(e); // node.next = dummyHead.next; // dummyHead.next = node; // 寫法三 // dummyHead.next = new Node(e, dummyHead.next); // size ++; // 寫法四 insert(0, e); } // 在鏈表指定索引出插入一個元素 public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("add or insert error. index < 0 or index > size"); } // 第一個prev就是dummyHead Node prev = dummyHead; // 不斷的搜索 一直經過next來進行檢索,找指定索引的節點的前一個元素 for (int i = 0; i < index ; i++) { prev = prev.next; } // 第一種方式 // Node node = new Node(e); // node.next = prev.next; // prev.next = node; // 第二種方式 prev.next = new Node(e, prev.next); size ++; } // 在鏈表尾部添加一個元素 public void addLast (E e) { insert(size, e); } // get public E get (int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size"); } Node cur = dummyHead.next; for (int i = 0; i < index ; i++) { cur = cur.next; } return cur.e; } // getFirst public E getFirst () { return get(0); } // getLast public E getLast () { return get(size - 1); } // set public void set (int index, E e) { if (index < 0 || index >= size) { throw new IllegalArgumentException("set error. index < 0 or index >= size"); } Node node = dummyHead.next; for (int i = 0; i < index; i++) { node = node.next; } node.e = e; } // contains public boolean contains (E e) { // 第一種方式 // Node node = dummyHead; // for (int i = 0; i < size - 1 ; i++) { // node = node.next; // // if (node.e.equals(e)) { // return true; // } // } // 第二種方式 Node node = dummyHead.next; while (node != null) { if (node.e.equals(e)) { return true; } else { node = node.next; } } return false; } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("鏈表長度:" + size + ",鏈表信息:"); // // 寫法一 // Node node = dummyHead.next; // while (node != null) { // sb.append(node + "->"); // node = node.next; // } // 寫法二 for (Node node = dummyHead.next; node != null ; node = node.next) { sb.append(node + "->"); } sb.append("NULL"); return sb.toString(); } }
Main
public class Main { public static void main(String[] args) { MyLinkedList<Integer> mkl = new MyLinkedList<Integer>(); for (int i = 1; i <= 5 ; i++) { mkl.addFirst(i); System.out.println(mkl); } mkl.insert(2, 88888); System.out.println(mkl); } }
鏈表元素的刪除
prev.next = delNode.next
,delNode.next = null
就完成了刪除,delNode = delNode.next
,(class: MyLinkedList, class: Main)
MyLinkedList
public class MyLinkedList<E> { // 隱藏內部實現,不須要讓用戶知道 private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } private Node dummyHead; private int size; public MyLinkedList () { dummyHead = new Node(null, null); size = 0; } // ... // 其它的構造函數,例如傳進來一個數組,將數組轉換爲鏈表 // 獲取鏈表中元素的個數 public int getSize () { return size; } // 返回當前鏈表是否爲空 public boolean isEmpty () { return size == 0; } // 在鏈表頭部添加一個元素 e public void addFirst (E e) { // 寫法一 // Node node = new Node(e, head); // head = node; // 寫法二 // Node node = new Node(e); // node.next = dummyHead.next; // dummyHead.next = node; // 寫法三 // dummyHead.next = new Node(e, dummyHead.next); // size ++; // 寫法四 insert(0, e); } // 在鏈表指定索引出插入一個元素 public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("add or insert error. index < 0 or index > size"); } // 第一個prev就是dummyHead Node prev = dummyHead; // 不斷的搜索 一直經過next來進行檢索,找指定索引的節點的前一個元素 for (int i = 0; i < index ; i++) { prev = prev.next; } // 第一種方式 // Node node = new Node(e); // node.next = prev.next; // prev.next = node; // 第二種方式 prev.next = new Node(e, prev.next); size ++; } // 在鏈表尾部添加一個元素 public void addLast (E e) { insert(size, e); } // get public E get (int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size"); } Node cur = dummyHead.next; for (int i = 0; i < index ; i++) { cur = cur.next; } return cur.e; } // getFirst public E getFirst () { return get(0); } // getLast public E getLast () { return get(size - 1); } // set public void set (int index, E e) { if (index < 0 || index >= size) { throw new IllegalArgumentException("set error. index < 0 or index >= size"); } Node node = dummyHead.next; for (int i = 0; i < index; i++) { node = node.next; } node.e = e; } // contains public boolean contains (E e) { // 第一種方式 // Node node = dummyHead; // for (int i = 0; i < size - 1 ; i++) { // node = node.next; // // if (node.e.equals(e)) { // return true; // } // } // 第二種方式 Node node = dummyHead.next; while (node != null) { if (node.e.equals(e)) { return true; } else { node = node.next; } } return false; } // remove public E remove (int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("remove error. index < 0 or index >= size"); } Node prev = dummyHead; for (int i = 0; i < index ; i++) { prev = prev.next; } Node delNode = prev.next; prev.next = delNode.next; size --; E e = delNode.e; delNode.next = null; return e; } // removeFirst public E removeFirst () { return remove(0); } // removeLast public E removeLast () { return remove(size - 1); } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("鏈表長度:" + size + ",鏈表信息:"); // // 寫法一 // Node node = dummyHead.next; // while (node != null) { // sb.append(node + "->"); // node = node.next; // } // 寫法二 for (Node node = dummyHead.next; node != null ; node = node.next) { sb.append(node + "->"); } sb.append("NULL"); return sb.toString(); } }
Main
public class Main { public static void main(String[] args) { MyLinkedList<Integer> mkl = new MyLinkedList<Integer>(); for (int i = 1; i <= 5 ; i++) { mkl.addFirst(i); System.out.println(mkl); } mkl.insert(2, 88888); System.out.println(mkl); mkl.remove(2); System.out.println(mkl); mkl.removeFirst(); System.out.println(mkl); mkl.removeLast(); System.out.println(mkl); } }
O(n)
:在只對鏈表頭進行操做時爲O(1)
O(n)
:在只對鏈表頭進行操做時爲O(1)
O(n)
O(n)
:只查鏈表頭的元素時爲O(1)
鏈表增刪改查的時間複雜度
鏈表還有諸多的改進的方式
addLast(e)
:O(n)
addFirst(e)
:O(1)
insert(index, e)
:O(n/2) = O(n)
removeLast()
:O(n)
removeFirst()
:O(1)
remove(index)
:O(n/2) = O(n)
set(index, e)
:O(n)
get(index)
:O(n)
contains(e)
:O(n)
find(e)
:O(n)
對鏈表進行添加操做時
O(n)
,O(1)
對鏈表進行刪除操做時
O(n)
,O(1)
對鏈表進行查詢操做時
O(n)
,O(1)
這些特性很符合棧的需求
首先定義接口IMyLinkedListStack
,
MyLinkedListStack
來實現這些接口。IMyLinkedListStack
void push(E e)
:添加一個元素E pop()
:移除一個元素E peek()
:查看棧頂的元素int getSize()
:獲取棧中實際的元素個數boolean isEmpty()
:判斷棧是否爲空(Interface: IMyLinkedListStack, class: MyLinkedList,
class: MyLinkedListStack, class: Main)
IMyLinkedListStack
public interface IMyLinkedListStack<E> { /** * @param e
*/ void push (E e); /** * @return e * 出棧 */ E pop (); /** * @return e * 查看棧頂的一個元素 */ E peek (); /** * @return size * 查看棧中實際元素的個數 */ int getSize (); /** * @return not empty * 判斷棧中是否爲空 */ boolean isEmpty (); }
3. `MyLinkedList`
public class MyLinkedList<E> { // 隱藏內部實現,不須要讓用戶知道 private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } private Node dummyHead; private int size; public MyLinkedList () { dummyHead = new Node(null, null); size = 0; } // ... // 其它的構造函數,例如傳進來一個數組,將數組轉換爲鏈表 // 獲取鏈表中元素的個數 public int getSize () { return size; } // 返回當前鏈表是否爲空 public boolean isEmpty () { return size == 0; } // 在鏈表頭部添加一個元素 e public void addFirst (E e) { // 寫法一 // Node node = new Node(e, head); // head = node; // 寫法二 // Node node = new Node(e); // node.next = dummyHead.next; // dummyHead.next = node; // 寫法三 // dummyHead.next = new Node(e, dummyHead.next); // size ++; // 寫法四 insert(0, e); } // 在鏈表指定索引出插入一個元素 public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("add or insert error. index < 0 or index > size"); } // 第一個prev就是dummyHead Node prev = dummyHead; // 不斷的搜索 一直經過next來進行檢索,找指定索引的節點的前一個元素 for (int i = 0; i < index ; i++) { prev = prev.next; } // 第一種方式 // Node node = new Node(e); // node.next = prev.next; // prev.next = node; // 第二種方式 prev.next = new Node(e, prev.next); size ++; } // 在鏈表尾部添加一個元素 public void addLast (E e) { insert(size, e); } // get public E get (int index) { if (index < 0 || index > size) { throw new IllegalArgumentException("get error. index < 0 or index > size"); } Node cur = dummyHead.next; for (int i = 0; i < index ; i++) { cur = cur.next; } return cur.e; } // getFirst public E getFirst () { return get(0); } // getLast public E getLast () { return get(size - 1); } // set public void set (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("set error. index < 0 or index > size"); } Node node = dummyHead.next; for (int i = 0; i < index; i++) { node = node.next; } node.e = e; } // contains public boolean contains (E e) { // 第一種方式 // Node node = dummyHead; // for (int i = 0; i < size - 1 ; i++) { // node = node.next; // // if (node.e.equals(e)) { // return true; // } // } // 第二種方式 Node node = dummyHead.next; while (node != null) { if (node.e.equals(e)) { return true; } else { node = node.next; } } return false; } // remove public E remove (int index) { if (index < 0 || index > size) { throw new IllegalArgumentException("remove error. index < 0 or index > size"); } Node prev = dummyHead; for (int i = 0; i < index ; i++) { prev = prev.next; } Node delNode = prev.next; prev.next = delNode.next; size --; E e = delNode.e; delNode.next = null; return e; } // removeFirst public E removefirst () { return remove(0); } // removeLast public E removeLast () { return remove(size - 1); } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("鏈表長度:" + size + ",鏈表信息:"); // // 寫法一 // Node node = dummyHead.next; // while (node != null) { // sb.append(node + "->"); // node = node.next; // } // 寫法二 for (Node node = dummyHead.next; node != null ; node = node.next) { sb.append(node + "->"); } sb.append("NULL"); return sb.toString(); } }
4. `MyLinkedListStack`
public class MyLinkedListStack<E> implements IMyLinkedListStack<E> { private MyLinkedList<E> mkl; public MyLinkedListStack () { mkl = new MyLinkedList<E>(); } /** * @param e 入棧 */ @Override public void push (E e) { mkl.addFirst(e); } /** * @return e * 出棧 */ @Override public E pop () { return mkl.removefirst(); } /** * @return e * 查看棧頂的一個元素 */ @Override public E peek () { return mkl.getFirst(); } /** * @return size * 查看棧中實際元素的個數 */ @Override public int getSize () { return mkl.getSize(); } /** * @return not empty * 判斷棧中是否爲空 */ @Override public boolean isEmpty () { return mkl.isEmpty(); } @Override public String toString () { int size = getSize(); StringBuilder sb = new StringBuilder(); sb.append("MyLinkedlistStack: 元素個數=" + size); sb.append(", stack top=[ "); for (int i = 0; i < size ; i++) { sb.append(mkl.get(i)); sb.append("->"); } sb.append("NULL ]"); return sb.toString(); } }
5. `Main`
public class Main {
public static void main(String[] args) { MyLinkedListStack<Integer> mkls = new MyLinkedListStack<Integer>(); for (int i = 1; i <= 5 ; i++) { mkls.push(i); System.out.println(mkls); } System.out.println(mkls.peek()); for (int i = 0; i < 5 ; i++) { System.out.println(mkls); mkls.pop(); } }
}
## 自定義數組棧對比自定義鏈表棧 1. 自定義數組棧與自定義鏈表棧的性能相差不多 1. 可是隨着操做的次數增加,數組棧會慢慢強過鏈表棧, 2. 自定義鏈表棧中有太多的 new 操做, 3. new 操做在有一些系統上比較耗費性能的, 4. 由於它在不停的在內存中尋找能夠開闢空間的地方來進行開闢空間, 5. 自定義數組棧中有比較多的擴容操做, 6. 因此這個比較是相對比較複雜的, 7. 和你的語法、操做系統、編譯器、解釋器都有關係, 8. 不過他們的時間複雜度都是`O(1)`級別的, 9. 因此他們之間的性能差別無非就 1-2 倍這樣, 10. 在最極端的狀況下 3-5 倍就已經很難了, 11. 不會有幾百倍的巨大的差別,由於畢竟他們的時間複雜度同樣。 ### 代碼示例 1. `(Interface: IStack, class: MyLinkedList,` 1. `class: MyLinkedListStack, class: MyArray,` 2. `class: MyStack, class: Main)` 2. `IStack`
public interface IStack<E> { /** * @param e * 入棧 */ void push (E e); /** * @return e * 出棧 */ E pop (); /** * @return e * 查看棧頂的一個元素 */ E peek (); /** * @return size * 查看棧中實際元素的個數 */ int getSize (); /** * @return not empty * 判斷棧中是否爲空 */ boolean isEmpty (); }
3. `MyLinkedList`
public class MyLinkedList<E> { // 隱藏內部實現,不須要讓用戶知道 private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } private Node dummyHead; private int size; public MyLinkedList () { dummyHead = new Node(null, null); size = 0; } // ... // 其它的構造函數,例如傳進來一個數組,將數組轉換爲鏈表 // 獲取鏈表中元素的個數 public int getSize () { return size; } // 返回當前鏈表是否爲空 public boolean isEmpty () { return size == 0; } // 在鏈表頭部添加一個元素 e public void addFirst (E e) { // 寫法一 // Node node = new Node(e, head); // head = node; // 寫法二 // Node node = new Node(e); // node.next = dummyHead.next; // dummyHead.next = node; // 寫法三 // dummyHead.next = new Node(e, dummyHead.next); // size ++; // 寫法四 insert(0, e); } // 在鏈表指定索引出插入一個元素 public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("add or insert error. index < 0 or index > size"); } // 第一個prev就是dummyHead Node prev = dummyHead; // 不斷的搜索 一直經過next來進行檢索,找指定索引的節點的前一個元素 for (int i = 0; i < index ; i++) { prev = prev.next; } // 第一種方式 // Node node = new Node(e); // node.next = prev.next; // prev.next = node; // 第二種方式 prev.next = new Node(e, prev.next); size ++; } // 在鏈表尾部添加一個元素 public void addLast (E e) { insert(size, e); } // get public E get (int index) { if (index < 0 || index > size) { throw new IllegalArgumentException("get error. index < 0 or index > size"); } Node cur = dummyHead.next; for (int i = 0; i < index ; i++) { cur = cur.next; } return cur.e; } // getFirst public E getFirst () { return get(0); } // getLast public E getLast () { return get(size - 1); } // set public void set (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("set error. index < 0 or index > size"); } Node node = dummyHead.next; for (int i = 0; i < index; i++) { node = node.next; } node.e = e; } // contains public boolean contains (E e) { // 第一種方式 // Node node = dummyHead; // for (int i = 0; i < size - 1 ; i++) { // node = node.next; // // if (node.e.equals(e)) { // return true; // } // } // 第二種方式 Node node = dummyHead.next; while (node != null) { if (node.e.equals(e)) { return true; } else { node = node.next; } } return false; } // remove public E remove (int index) { if (index < 0 || index > size) { throw new IllegalArgumentException("remove error. index < 0 or index > size"); } Node prev = dummyHead; for (int i = 0; i < index ; i++) { prev = prev.next; } Node delNode = prev.next; prev.next = delNode.next; size --; E e = delNode.e; delNode.next = null; return e; } // removeFirst public E removefirst () { return remove(0); } // removeLast public E removeLast () { return remove(size - 1); } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("鏈表長度:" + size + ",鏈表信息:"); // // 寫法一 // Node node = dummyHead.next; // while (node != null) { // sb.append(node + "->"); // node = node.next; // } // 寫法二 for (Node node = dummyHead.next; node != null ; node = node.next) { sb.append(node + "->"); } sb.append("NULL"); return sb.toString(); } }
4. `MyLinkedListStack`
public class MyLinkedListStack<E> implements IStack<E> { private MyLinkedList<E> mkl; public MyLinkedListStack () { mkl = new MyLinkedList<E>(); } /** * @param e 入棧 */ @Override public void push (E e) { mkl.addFirst(e); } /** * @return e * 出棧 */ @Override public E pop () { return mkl.removefirst(); } /** * @return e * 查看棧頂的一個元素 */ @Override public E peek () { return mkl.getFirst(); } /** * @return size * 查看棧中實際元素的個數 */ @Override public int getSize () { return mkl.getSize(); } /** * @return not empty * 判斷棧中是否爲空 */ @Override public boolean isEmpty () { return mkl.isEmpty(); } @Override public String toString () { int size = getSize(); StringBuilder sb = new StringBuilder(); sb.append("MyLinkedlistStack: 元素個數=" + size); sb.append(", stack top=[ "); for (int i = 0; i < size ; i++) { sb.append(mkl.get(i)); sb.append("->"); } sb.append("NULL ]"); return sb.toString(); } }
5. `MyArray`
public class MyArray<E> { private E [] data; private int size; // 構造函數,傳入數組的容量capacity構造Array public MyArray (int capacity) { data = (E[])new Object[capacity]; size = 0; } // 無參數的構造函數,默認數組的容量capacity=10 public MyArray () { // this( capacity: 10); this(10); } // 獲取數組中的元素實際個數 public int getSize () { return size; } // 獲取數組的總容量 public int getCapacity () { return data.length; } // 返回數組是否爲空 public boolean isEmpty () { return size == 0; } // 從新給數組擴容 private void resize (int newCapacity) { E[] newData = (E[])new Object[newCapacity]; int index = size - 1; while (index > -1) { newData[index] = get(index); index --; } data = newData; } // 給數組添加一個新元素 public void add (E e) { if (size == data.length) { // throw new IllegalArgumentException("add error. Array is full."); resize(2 * data.length); } data[size] = e; size++; } // 向全部元素後添加一個新元素 (與 add方法功能同樣) push public void addLast (E e) { // 複用插入元素的方法 insert(size, e); } // 在全部元素前添加一個新元素 unshift public void addFirst (E e) { insert(0, e); } // 在index索引的位置插入一個新元素e public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("insert error. require index < 0 or index > size"); } if (size == data.length) { // throw new IllegalArgumentException("add error. Array is full."); resize(2 * data.length); } for (int i = size - 1; i >= index; i--) { data[i + 1] = data[i]; } data[index] = e; size++; } // 獲取index索引位置的元素 public E get (int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size "); } return data[index]; } // 獲取數組中第一個元素(純查看) public E getFirst () { return get(0); } // 獲取數組中最後一個元素(純查看) public E getLast () { return get(size - 1); } // 修改index索引位置的元素爲e public void set (int index, E e) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size "); } data[index] = e; } // 查找數組中是否有元素e public boolean contain (E e) { for (int i = 0; i < size; i++) { // if (data[i] == e) { // 值比較 用 == if (data[i].equals(e)) { // 引用比較 用 equals() return true; } } return false; } // 查找數組中元素e所在的索引,若是不存在元素e,則返回-1 public int find (E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return i; } } return -1; } // 查找數組中全部元素e所在的索引,最後返回存放 全部索引值的 自定義數組 public MyArray findAll (E e) { MyArray ma = new MyArray(20); for (int i = 0; i < size; i++) { if (data[i].equals(e)) { ma.add(i); } } return ma; // int[] result = new int[ma.getSize()]; // for (int i = 0; i < ma.getSize(); i++) { // result[i] = ma.get(i); // } // // return result; } // 從數組中刪除第一個元素, 返回刪除的元素 public E removeFirst () { return remove(0); } // 從數組中刪除最後一個元素, 返回刪除的元素 public E removeLast () { return remove(size - 1); } // 從數組中刪除第一個元素e public void removeElement (E e) { int index = find(e); if (index != -1) { remove(index); } // if (contain(e)) { // int index = find(e); // remove(index); // } } // 從數組中刪除全部元素e public void removeAllElement (E e) { int index = find(e); while (index != -1) { remove(index); index = find(e); } // while (contain(e)) { // removeElement(e); // } } // 從數組中刪除index位置的元素, 返回刪除的元素 public E remove (int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size "); } E temp = data[index]; for (int i = index; i < size - 1; i++) { data[i] = data[i + 1]; } // for (int i = index + 1; i < size; i++) { // data[i - 1] = data[i]; // } size --; // data[size] = 0; data[size] = null; // 防止複雜度震盪 防止容量爲4,size爲1時,data.length / 2 爲 0 if(size == data.length / 4 && data.length / 2 != 0) { resize(data.length / 2); } return temp; } @Override // @Override: 方法名 日期-開發人員 public String toString () { StringBuilder sb = new StringBuilder(); String arrInfo = "Array: size = %d,capacity = %d\n"; sb.append(String.format(arrInfo, size, data.length)); sb.append('['); for (int i = 0; i < size - 1; i ++) { sb.append(data[i]); sb.append(','); } sb.append(data[size - 1]); sb.append(']'); return sb.toString(); } }
6. `MyStack`
public class MyStack<E> implements IStack<E> { // 借用自定義個動態數組 private MyArray<E> ma; public MyStack () { ma = new MyArray<E>(); } public MyStack (int capacity) { ma = new MyArray<E>(capacity); } /** * @param e * @return 入棧 */ @Override public void push(E e) { ma.addLast(e); } /** * @return 出棧 */ @Override public E pop() { return ma.removeLast(); } /** * @return 查看棧頂的元素 */ @Override public E peek() { return ma.getLast(); } /** * @return 獲取棧中實際元素的個數 */ @Override public int getSize() { return ma.getSize(); } /** * @return 判斷棧是否爲空 */ @Override public boolean isEmpty() { return ma.isEmpty(); } // 返回棧的容量 public int getCapacity () { return ma.getCapacity(); } @Override // @Override: 方法名 日期-開發人員 public String toString () { int size = ma.getSize(); // int capacity = ma.getCapacity(); StringBuilder sb = new StringBuilder(); // String arrInfo = "Stack: size = %d,capacity = %d\n"; // sb.append(String.format(arrInfo, size, capacity)); sb.append("Stack: ["); for (int i = 0; i < size - 1; i ++) { sb.append(ma.get(i)); sb.append(','); } if (!ma.isEmpty()) { sb.append(ma.getLast()); } sb.append("] right is stack top !"); return sb.toString(); } }
7. `Main`
import java.util.Random; public class Main { private static double testStack (IStack<Integer> s, int openCount) { long startTime = System.nanoTime(); Random random = new Random(); for (int i = 1; i <= openCount ; i++) { s.push(random.nextInt(Integer.MAX_VALUE)); } // while (!s.isEmpty()) { s.pop(); } // .. long endTime = System.nanoTime(); return (endTime - startTime) / 1000_000_000.0; } public static void main(String[] args) { MyLinkedListStack<Integer> mkls = new MyLinkedListStack<Integer>(); MyStack<Integer> ms = new MyStack<Integer>(); double msTime = testStack(ms, 100000); double mklsTime = testStack(mkls, 100000); System.out.println("MyStack,time:" + msTime + "s."); System.out.println("MyLinkedListStack,time:" + mklsTime + "s."); } }
## 使用鏈表來實現隊列 1. 對鏈表進行添加操做時 1. 時間複雜度爲`O(n)`, 2. 只對鏈表頭進行操做時爲`O(1)`, 3. 對鏈表尾部進行操做時爲`O(n)` 2. 對鏈表進行刪除操做時 1. 時間複雜度爲`O(n)`, 2. 只對鏈表頭進行操做時爲`O(1)`, 3. 對鏈表尾部進行操做時爲`O(n)` 3. 對鏈表進行查詢操做時 1. 時間複雜度爲`O(n)`, 2. 只查鏈表頭的元素時爲`O(1)`, 3. 查鏈表尾部的元素時爲`O(n)` 4. 隊列中的操做 1. 在線性結構的一端插入元素, 2. 在另一端刪除元素, 3. 因此必然會在線性結構的兩端同時操做, 4. 此時就會有一端的操做的複雜度是`O(n)`級別, 5. 這個問題在用自定義數組實現時也遇到了, 6. 也正由於如此產生了循環隊列, 7. 經過改進使用數組來實現隊列的方式, 8. 因此鏈表也能夠進行改進。 5. 改進鏈表 1. 不能使用以前的鏈表來進行隊列的實現, 2. 設置一個 head 變量指向鏈表的頭部第一個節點, 3. 設置一個 tail 變量指向鏈表的尾部第一個節點, 4. 有了 tail 這個變量,那麼在鏈表的尾部添加一個元素很是容易, 5. 有了 head 和 tail 以後在兩端添加節點是很是容易的, 6. 可是沒法使用`O(1)`去刪除尾部的節點, 7. 從 tail 這一端刪除元素並不容易, 8. 可是若是將 head 做爲隊首,將 tail 做爲隊尾, 9. 隊列操做是從隊尾進隊首出的, 10. 只須要從隊尾 tail 插入元素,從隊首 head 刪除元素, 11. 這樣一來就能夠實現隊列的功能。 6. 鏈表中再也不有插入的功能因此不須要 dummyHead 1. 可是因爲沒有 dummyHead,因此須要注意鏈表爲空的狀況。 7. 讓自定義動態數組隊列與自定義鏈表隊列進行對比 1. 自定義動態數組隊列的性能最差 2. 自定義動態數組循環隊列與自定義鏈表隊列的性能相近。 8. 與鏈表相關的有一個很是重要的內容 1. 就是遞歸,由於鏈表自己具備自然的遞歸性質, 2. 同時它又是一種很是簡單的數據結構, 3. 因此鏈表是一種很是好的 4. 研究學習遞歸的邏輯機制的的數據結構。 ### 代碼示例 1. `(Interface: IMyQueue, class: MyArray, class: MyQueue,` 1. `class: MyLoopQueue, class: MyLinkedListQueue, class: Main)` 2. `IMyQueue`
public interface IMyQueue<E> { /** - @param e - 入隊 */ void enqueue (E e); /** - @return e - 出隊 */ E dequeue (); /** - @return e - 查看隊首的元素 */ E getFront (); /** - @return number - 獲取隊列中的實際元素個數 */ int getSize (); /** - @return bool - 獲取隊列是否爲空的bool值 */ boolean isEmpty (); }
3. `MyArray`
public class MyArray<E> { private E [] data; private int size; // 構造函數,傳入數組的容量capacity構造Array public MyArray (int capacity) { data = (E[])new Object[capacity]; size = 0; } // 無參數的構造函數,默認數組的容量capacity=10 public MyArray () { // this( capacity: 10); this(10); } // 獲取數組中的元素實際個數 public int getSize () { return size; } // 獲取數組的總容量 public int getCapacity () { return data.length; } // 返回數組是否爲空 public boolean isEmpty () { return size == 0; } // 從新給數組擴容 private void resize (int newCapacity) { E[] newData = (E[])new Object[newCapacity]; int index = size - 1; while (index > -1) { newData[index] = get(index); index --; } data = newData; } // 給數組添加一個新元素 public void add (E e) { if (size == data.length) { // throw new IllegalArgumentException("add error. Array is full."); resize(2 * data.length); } data[size] = e; size++; } // 向全部元素後添加一個新元素 (與 add方法功能同樣) push public void addLast (E e) { // 複用插入元素的方法 insert(size, e); } // 在全部元素前添加一個新元素 unshift public void addFirst (E e) { insert(0, e); } // 在index索引的位置插入一個新元素e public void insert (int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("insert error. require index < 0 or index > size"); } if (size == data.length) { // throw new IllegalArgumentException("add error. Array is full."); resize(2 * data.length); } for (int i = size - 1; i >= index; i--) { data[i + 1] = data[i]; } data[index] = e; size++; } // 獲取index索引位置的元素 public E get (int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size "); } return data[index]; } // 獲取數組中第一個元素(純查看) public E getFirst () { return get(0); } // 獲取數組中最後一個元素(純查看) public E getLast () { return get(size - 1); } // 修改index索引位置的元素爲e public void set (int index, E e) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size "); } data[index] = e; } // 查找數組中是否有元素e public boolean contain (E e) { for (int i = 0; i < size; i++) { // if (data[i] == e) { // 值比較 用 == if (data[i].equals(e)) { // 引用比較 用 equals() return true; } } return false; } // 查找數組中元素e所在的索引,若是不存在元素e,則返回-1 public int find (E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return i; } } return -1; } // 查找數組中全部元素e所在的索引,最後返回存放 全部索引值的 自定義數組 public MyArray findAll (E e) { MyArray ma = new MyArray(20); for (int i = 0; i < size; i++) { if (data[i].equals(e)) { ma.add(i); } } return ma; // int[] result = new int[ma.getSize()]; // for (int i = 0; i < ma.getSize(); i++) { // result[i] = ma.get(i); // } // // return result; } // 從數組中刪除第一個元素, 返回刪除的元素 public E removeFirst () { return remove(0); } // 從數組中刪除最後一個元素, 返回刪除的元素 public E removeLast () { return remove(size - 1); } // 從數組中刪除第一個元素e public void removeElement (E e) { int index = find(e); if (index != -1) { remove(index); } // if (contain(e)) { // int index = find(e); // remove(index); // } } // 從數組中刪除全部元素e public void removeAllElement (E e) { int index = find(e); while (index != -1) { remove(index); index = find(e); } // while (contain(e)) { // removeElement(e); // } } // 從數組中刪除index位置的元素, 返回刪除的元素 public E remove (int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get error. index < 0 or index >= size "); } E temp = data[index]; for (int i = index; i < size - 1; i++) { data[i] = data[i + 1]; } // for (int i = index + 1; i < size; i++) { // data[i - 1] = data[i]; // } size --; // data[size] = 0; data[size] = null; // 防止複雜度震盪 防止容量爲4,size爲1時,data.length / 2 爲 0 if(size == data.length / 4 && data.length / 2 != 0) { resize(data.length / 2); } return temp; } @Override // @Override: 方法名 日期-開發人員 public String toString () { StringBuilder sb = new StringBuilder(); String arrInfo = "Array: size = %d,capacity = %d\n"; sb.append(String.format(arrInfo, size, data.length)); sb.append('['); for (int i = 0; i < size - 1; i ++) { sb.append(data[i]); sb.append(','); } sb.append(data[size - 1]); sb.append(']'); return sb.toString(); } }
4. `MyQueue`
public class MyQueue<E> implements IMyQueue<E> { private MyArray<E> ma; public MyQueue () { ma = new MyArray<E>(); } public MyQueue (int capacity) { ma = new MyArray<E>(capacity); } /** - @param e - 入隊 */ @Override public void enqueue (E e) { ma.addLast(e); } /** - @return e - 出隊 */ @Override public E dequeue () { return ma.removeFirst(); } /** - @return e - 查看隊首的元素 */ @Override public E getFront () { return ma.getFirst(); } /** - @return number - 獲取隊列中的實際元素個數 */ @Override public int getSize () { return ma.getSize(); } /** - @return bool - 獲取隊列是否爲空的bool值 */ @Override public boolean isEmpty () { return ma.isEmpty(); } // 獲取隊列容量 public int getCapacity () { return ma.getCapacity(); } @Override public String toString () { int size = ma.getSize (); StringBuilder sb = new StringBuilder(); sb.append("Queue: head ["); for (int i = 0; i < size - 1; i ++) { sb.append(ma.get(i)); sb.append(','); } if(!isEmpty()) { sb.append(ma.getLast()); } sb.append("] foot. left is queue top!"); return sb.toString(); } }
5. `MyLoopQueue`
public class MyLoopQueue<E> implements IMyQueue<E> { private E[] data; private int front, tail; private int size; public MyLoopQueue (int capacity) { // 這個數組的容量爲 傳進來的指定容量+1, // 由於會有意識的浪費一個空間,只有+1後, // 才能裝下用戶指望傳進來的全部數據 data = (E[])new Object[capacity + 1]; front = tail = size = 0; } public MyLoopQueue () { this(10); } public int getCapacity () { return data.length - 1; } private void resize (int newCapacity) { E[] newData = (E[]) new Object[newCapacity + 1]; for (int i = 0; i < size; i++) { // 索引可能會越界,因而就要取餘一下, // 若是越界了,就從隊首開始 newData[i] = data[(front + i) % data.length]; } data = newData; front = 0; tail = size; } /** * @param e 入隊 */ @Override public void enqueue(E e) { if ((tail + 1) % data.length == front) { resize(getCapacity() * 2); } data[tail] = e; // tail在隊列中循環 tail = (tail + 1) % data.length; size ++; } /** * @return e * 出隊 */ @Override public E dequeue() { if(isEmpty()) { throw new IllegalArgumentException("can't dequeue from an empty queue."); } E e = data[front]; data[front] = null; front = (front + 1) % data.length; size -- ; if (getCapacity() / 4 == size && getCapacity() / 2 != 0) { resize(getCapacity() / 2); } return e; } /** * @return e * 查看隊首的元素 */ @Override public E getFront() { if (isEmpty()) { throw new IllegalArgumentException("queue is empty."); } return data[front]; } /** * @return number * 獲取隊列中的實際元素個數 */ @Override public int getSize() { return size; } /** * @return bool * 獲取隊列是否爲空的bool值 */ @Override public boolean isEmpty() { return front == tail; } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append(String.format("Queue: size = %d,capacity = %d \n", size, getCapacity())); sb.append("Queue: head ["); // 第一種遍歷方式 // for (int i = 0; i < size - 1; i ++) { // sb.append(data[(front + i) % data.length]); // sb.append(','); // } // if(!isEmpty()) { // sb.append(data[(front + size - 1) % data.length]); // } // 第二種遍歷方式 for (int i = front; i != tail ; i = (i + 1) % data.length) { sb.append(data[i]); if ((i + 1) % data.length != tail) { sb.append(','); } } sb.append("] foot. left is queue top!"); sb.append("\n"); return sb.toString(); } }
6. `MyLinkedListQueue`
public class MyLinkedListQueue<E> implements IMyQueue<E> { private class Node { public E e; public Node next; public Node (E e, Node next) { this.e = e; this.next = next; } public Node (E e) { this(e, null); } public Node () { this(null, null); } @Override public String toString () { return e.toString(); } } private Node head, tail; private int size; public MyLinkedListQueue () { head = tail = null; size = 0; } /** * @param e 入隊 */ @Override public void enqueue(E e) { // // 第一種方式 // Node node = new Node(e); // node.next = tail; // tail = node; // 第二種方式 // head = new Node(e, head); // 鏈表尾部爲空 if (tail == null) { tail = new Node(e); head = tail; } else { tail.next = new Node(e); tail = tail.next; } size ++; // 不須要管頭節點,由於頭節點是不須要動的。 } /** * @return e * 出隊 */ @Override public E dequeue() { if (isEmpty()) { throw new IllegalArgumentException("can not dequeue from an empty queue."); } Node node = head; head = head.next; node.next = null; if (head == null) { tail = null; } size --; return node.e; } /** * @return e * 查看隊首的元素 */ @Override public E getFront() { if (isEmpty()) { throw new IllegalArgumentException("can not dequeue from an empty queue."); } return head.e; } /** * @return number * 獲取隊列中的實際元素個數 */ @Override public int getSize() { return size; } /** * @return bool * 獲取隊列是否爲空的bool值 */ @Override public boolean isEmpty() { return size == 0; } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("MyLinkedListQueue: 元素個數=" + size); sb.append(", queue front [ "); for (Node node = head; node != null; node = node.next) { sb.append(node); sb.append("->"); } sb.append("NULL ] tail"); return sb.toString(); } public static void main(String[] args) { MyLinkedListQueue<Integer> mkls = new MyLinkedListQueue<Integer>(); for (int i = 1; i <= 5 ; i++) { mkls.enqueue(i); System.out.println(mkls); } System.out.println(mkls.getFront()); for (int i = 0; i < 5 ; i++) { System.out.println(mkls); mkls.dequeue(); } } }
7. `Main`
import java.util.Random; public class Main { private static double testQueue (IMyQueue<Integer> q, int openCount) { long startTime = System.nanoTime(); Random random = new Random(); for (int i = 1; i <= openCount ; i++) { q.enqueue(random.nextInt(Integer.MAX_VALUE)); } // while (!q.isEmpty()) { q.dequeue(); } // .. long endTime = System.nanoTime(); return (endTime - startTime) / 1000_000_000.0; } public static void main(String[] args) { IMyQueue<Integer> mq = new MyQueue<Integer>(); IMyQueue<Integer> mlq = new MyLoopQueue<Integer>(); IMyQueue<Integer> mklq = new MyLinkedListQueue<Integer>(); double mqTime = testQueue(mq, 100000); double mlqTime = testQueue(mlq, 100000); double mklqTime = testQueue(mklq, 100000); System.out.println("MyQueue,time:" + mqTime + "s."); System.out.println("MyLoopQueue,time:" + mlqTime + "s."); System.out.println("MyLinkedListQueue,time:" + mklqTime + "s."); } }