【從蛋殼到滿天飛】JAVA 數據結構解析和算法實現-鏈表

思惟導圖

前言

【從蛋殼到滿天飛】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

鏈表 Linked List

  1. 鏈表是最基礎的動態數據結構
  2. 鏈表是很是重要的線性數據結構github

    1. 如下三種,底層都是依託靜態數組,靠 resize 解決固定容量問題。
    2. 動態數組:所謂動態,是從用戶的角度上來看的。
    3. 隊列
  3. 鏈表是真正的動態數據結構面試

    1. 它是數據結構中的一個重點,
    2. 也有多是一個難點,
    3. 它是最簡單的一種動態數據結構,
    4. 其它更高級的動態數據結構有 二分搜索樹、Trie、
    5. 平衡二叉樹、AVL、紅黑樹等等,
    6. 熟悉了最簡單的動態數據結構,
    7. 那麼對於更高級的也會比較容易掌握了。
  4. 對於鏈表來講它涉及到了計算機領域一個很是重要的概念算法

    1. 更深刻的理解引用(或者指針),
    2. 這個概念和內存相關,
    3. 在 java 裏面不須要手動的管理內存,
    4. 可是對鏈表這種數據結構更加深刻的理解,
    5. 可讓你對 引用、指針甚至計算機系統中
    6. 和內存管理相關不少話題有更加深刻的認識。
  5. 鏈表自己也是有它很是清晰的遞歸結構的,數組

    1. 只不過因爲鏈表這種數據結構它自己是一種線性的數據結構,
    2. 因此依然能夠很是容易的使用循環的方式來對鏈表進行操做,
    3. 可是鏈表自己因爲它天生也是具備這種遞歸結構的性質,
    4. 因此它也能讓你更加深刻的理解遞歸機制相應的這種數據結構,
    5. 在學習更加複雜的數據結構的時候,
    6. 對遞歸這種邏輯機制深刻的理解是必不可缺的。
  6. 鏈表這種數據結構自己就具備功能性數據結構

    1. 它能夠用來組成更加複雜的數據結構,
    2. 好比 圖結構、hash 表、棧(鏈表版)、隊列(鏈表版),
    3. 因此他能夠輔助組成其它數據結構。

什麼是鏈表

  1. 數據存儲在「節點」(Node)中app

    1. 把數據存在一種單獨的結構中,
    2. 這個結構一般管它叫作節點,
    3. 對於鏈表節點來講他一般只有兩部分,
    4. 一部分就是存儲真正的數據,
    5. 另外一部分存儲的是當前節點的下一個節點,
    6. 鏈表其實就像一個火車,每個節點都是一節車箱,
    7. 在車箱中存儲數據,而車箱和車箱之間還會進行鏈接,
    8. 以使得這些數據是整合在一塊兒的,
    9. 這樣用戶能夠方便的在全部的這些數據上進行查詢等等其它的操做,
    10. 數據與數據之間鏈接就是用下面的這個 next 來完成的
    class Node {
          E e;
          Node next;
       }
  2. 鏈表的優勢

    1. 真正的動態,不須要處理固定容量的問題,
    2. 它不像靜態數組那樣一會兒 new 出來一片空間,
    3. 也根本不須要考慮這個空間是否是不夠用,
    4. 也根本不用去考慮這個空間是否是開多了。
  3. 對於鏈表來講,你須要多少個數據。

    1. 你就能夠生成多少個節點,
    2. 把它們掛接起來了,這就是所謂的動態。
  4. 鏈表的缺點

    1. 和數組相比,它喪失了隨機訪問的能力。
    2. 不能像數組那樣給一個索引
    3. 就能直接訪問對應索引位置的元素,
    4. 這是由於從底層機制上數組所開闢的空間
    5. 在內存裏是連續分佈的,
    6. 因此纔可以直接去向索引對應的偏移
    7. 計算出相應的數據所存儲的內存地址,
    8. 而後就可以直接用O(1)的複雜度取出這個元素,
    9. 可是鏈表不一樣,鏈表因爲是靠 next 一層一層鏈接的,
    10. 因此在計算機的底層,每個節點他所在的內存的位置是不一樣的,
    11. 只可以靠這個 next 來一層一層的找到這個元素,
    12. 這就是鏈表最大的缺點。

數組和鏈表的對比

  1. 數組

    1. 數組最好永遠索引有語意的狀況,如 scores[2]
    2. 最大的優勢:支持快速查詢
  2. 鏈表

    1. 鏈表不適合用於索引有語意的狀況。
    2. 最大的優勢:動態存儲。
  3. 對比

    1. 數組也能夠沒有語意,並非全部的時候索引是有語意的,
    2. 也並非全部有語意的這樣的一個標誌就適合作索引,如身份證號,
    3. 將一個靜態數組改變爲一個動態數組,
    4. 就是在應付不方便使用索引的時候有關數據存儲的問題,
    5. 對於這樣的存儲數據的需求使用鏈表是更合適的,
    6. 由於鏈表最大的優勢是動態存儲。
  4. 要清楚何時使用數組這樣的靜態數據結構,

    1. 何時使用鏈表這類的動態數據結構。
  5. 簡單的代碼示例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();
                   }
             }
       }

在鏈表中進行添加、插入操做

  1. 鏈表是經過節點來裝載元素

    1. 而且節點和節點之間會進行鏈接。
    2. 若是想訪問這條鏈表中全部的節點,
    3. 那麼就必須把鏈表的頭給存儲起來,
    4. 一般這個鏈表的頭叫作的 head,
    5. 應該說在MyLinkedList
    6. 應該有一個變量指向鏈表中的第一個節點。
  2. 給自定義數組添加元素是從數組尾部開始添加,

    1. 給鏈表添加元素是從數組頭部開始添加,
    2. 由於自定義數組有維護一個 size,
    3. 這個 size 指向的是下一個待添加元素的位置,
    4. 因此你直接將 size 作索引直接賦值便可,
    5. 有 size 這個變量在跟蹤數組的尾巴,
    6. 而對於鏈表來講 有設置一個鏈表的頭這樣的一個變量,
    7. 也就是說有一個變量在跟蹤鏈表的頭,
    8. 並無一個變量去跟蹤鏈表的尾巴,
    9. 因此在鏈表頭部添加元素是很是方便。
  3. 添加操做原理

    1. 將一個新的元素掛接到鏈表頭部,
    2. 同時不破壞如今鏈表的這個結構,
    3. 添加一個新元素 666,它的名字叫 node,
    4. 就只須要使用 node.next = head
    5. 也就是將新添加的元素的 next 指向鏈表的頭,
    6. 這個時候新添加的元素也成爲新的鏈表頭,
    7. 也就是head = node
    8. 這樣 head 就會指向新的元素 666,也就是 node 這個節點,
    9. 如此一來就完成了將 node 這個節點插入了整個鏈表的頭部,
    10. node 這個變量是在一個函數中聲明的,
    11. 因此函數結束以後這個變量的做用域也就結束了,
    12. 鏈表的 head 也等於 node,
    13. 鏈表中就新增了一個新元素。
  4. 在鏈表頭部添加元素很是簡單,

    1. 只須要建立節點,讓節點的 next 指向 head,
    2. 而後讓 head 從新指向這個節點,也就是讓 head 變成新的元素,
    3. 由於鏈表是從頭部添加元素的。
  5. 在鏈表中間添加元素,

    1. 定義一個 prev 的變量指向中間元素的前一個元素,
    2. 而後讓新增長的元素這樣,node.next = prev.next
    3. 以後這樣,prev.next = node
    4. 這樣一來就在 prev 這個元素和本來 prev 的下一個元素之間
    5. 插入了一個新元素,叫作 node,
    6. 整個過程就是將 prev 的 next(下一個元素)的引用
    7. 傳遞給新元素的 next(下一個元素),
    8. 而後將 prev 的 next(下一個元素)變動爲這個新元素,
    9. 最後就將新元素插入到中間了。
    10. 這個過程的關鍵是找到待添加的節點的前一個節點,
    11. 這個就是 prev 變量要作的事情。
  6. 在鏈表的操做中不少時候順序很是重要,

    1. 如在鏈表中插入一個元素,在指定的元素後面插入一個元素,
    2. 而且不影響整個鏈表結構,和在鏈表中間添加元素同樣,
    3. 把本來的node.next = prev.nextprev.next = node
    4. 的順序變一下,prev.next = node在前,
    5. node.next = prev.next 在後,這樣一來邏輯就不成立了,
    6. 鏈表不只斷開了,並且新節點的 next 指向的是本身,死循環了,
    7. 因此同樣的代碼,順序顛倒了,獲得的結果就是錯誤的。
  7. 在鏈表的 index(0-based)位置添加元素 e

    1. 經過索引來操做鏈表,在鏈表中不是一個經常使用的操做,
    2. 由於在選擇使用鏈表的時候一般就選擇不使用索引了,
    3. 可是這個需求在面試等一些考題中出現的機率很大,
    4. 因此他的主要做用更多的是一個練習。

學習方式

  1. 若是剛接觸鏈表,對鏈表不熟悉,

    1. 而後不少這種操做在腦子裏不能很是快的反應出來,
    2. 不清楚他的意義是什麼,那你可使用紙筆來稍微畫一下,
    3. 每一步程序邏輯的執行意味着當前這個鏈表變成了什麼樣子,
    4. 這個過程可以幫助你更加深刻的理解程序,
    5. 包括在調試的時候來看你的程序到底爲何出了錯誤時,
    6. 很是很是有幫助的,不要犯懶,爲了更加深刻的理解你的程序,
    7. 不能只盯着你的代碼去想,必定要寫寫畫畫,
    8. 必要的時候甚至根據你的程序進行具體的調試,
    9. 這些都是很是重要很是有幫助的。

代碼示例 (class: MyLinkedList)

  1. 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);
             }
       }

給鏈表設立虛擬頭節點

  1. 在鏈表中進行指定索引處插入元素時

    1. 對鏈表頭插入元素與對鏈表其它位置插入元素的邏輯有區別,
    2. 因此就致使每次操做都須要對索引進行判斷,
    3. 若是你是在索引 0 的位置插入元素,那麼就沒有 prev(前一個元素),
    4. 天然就不可使用node.next = prev.nextprev.next = node了,
    5. 那時候你能夠直接使用node.next = headhead = node
    6. 不過已經有了向鏈表頭添加元素的方法了,
    7. 因此向頭部插入元素時根本就不須要這麼作,
    8. 這時候能夠經過給鏈表設立虛擬頭節點來解決這個問題。
  2. 爲何對鏈表頭插入元素那麼特殊?

    1. 由於插入元素時,必須要找到該索引位置的節點以前的一個節點(prev),
    2. 而鏈表頭(head)以前並無任何節點,因此邏輯上就會特殊一些。
    3. 不過在鏈表的具體實現中有一個很是經常使用的技巧,
    4. 能夠把鏈表頭的這種特殊的操做與其它的操做一塊兒統一塊兒來,
    5. 其實這個方法的想法很簡單,既然它沒有鏈表頭以前的節點,
    6. 那就本身造一個鏈表頭以前的節點,
    7. 這個鏈表頭以前的節點不會用來存儲任何元素,存儲的數據爲空,
    8. 這個空節點就稱之爲整個鏈表頭的 head,也能夠叫它 dummyHead,
    9. 由於它就是一個假的 head,它也是爲鏈表設立的虛擬頭節點,
    10. 這樣一來 鏈表的第一個元素就是 dummyHead 的 next 所對應的那個節點,
    11. 由於 dummyHead 這個位置的元素是根本不存在的,
    12. 對用戶來說也是根本沒有意義的,
    13. 這只是爲了編寫邏輯方便而出現的一個虛擬的頭節點,
    14. 因此一個這樣的內部機制對用戶來講也是不可見的,
    15. 用戶不知道鏈表中有沒有虛擬的頭節點,
    16. 這和自定義的循環隊列有點像,自定義循環隊列中有一個不可用的空間,
    17. 有意識地去浪費一個空間,爲了編寫邏輯更加的方便,
    18. 從而也讓性能在總體上獲得了提高。
  3. 有了 dummyHead 以後就不須要處理頭節點這個特殊的操做

    1. 由於當你在頭部插入元素時,能夠這樣
    2. node.next = dummyHead.nextdummyHead.next = node
    3. 這樣你就解決了每次插入時都要進行一次邏輯判斷了,
    4. 這樣一來就說明了鏈表中全部的節點都有前一個節點了,
    5. 在初始的時候 dummyHead 指向的是索引爲 0 的元素前一個位置的節點。
  4. 鏈表操做的實際原理

    1. 在沒有使用虛擬頭節點以前,一直都是在鏈表頭 head 這個地方不停的添加節點,
    2. 而後將 head 這個變量不停的指向新添加的節點 node.next = head.next;head = node;
    3. 在使用了虛擬頭節點以後,
    4. 一直是在鏈表虛擬頭 dummyHead 這個地方後面一個位置不停的插入新節點,
    5. dummyHead 從未變更過,永遠在鏈表的第一個位置,
    6. node.next = dummyHead.next; dummyHead.next = node;
    7. 可是dummyHead.next纔是鏈表中第一個實際記錄的節點,
    8. 用戶會不知道虛擬節點的存在,由於 dummyHead 並不算在鏈表的實際節點中,
    9. 也就是 size 中不會維護 dummyHead,dummyHead 的存在是爲了維護一致的插入操做。

代碼示例 (class: MyLinkedList)

  1. 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);
             }
       }

在鏈表中進行遍歷、查詢、修改操做

  1. 若是要找指定索引元素的前一個節點

    1. 那麼就要從dummyHead開始遍歷,
    2. 若是要找指定索引元素的那個節點,
    3. 那麼就要從dummyHead.next開始遍歷。

代碼示例(class: MyLinkedList, class: Main)

  1. 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();
             }
       }
  2. 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);
             }
       }

在鏈表中進行刪除操做

  1. 鏈表元素的刪除

    1. 假設要刪除索引爲 2 位置的元素
    2. 就是要索引爲 2 位置的元素的前一個元素,
    3. 還要索引爲 2 位置的元素的後一個元素,
    4. 讓前一個元素的 next 等於後一個元素,
    5. 也就是前一個元素的 next 等於索引爲 2 的元素的 next。
    6. 也就是讓 prev 指向待刪除的那個節點的前一個節點,
    7. prev 這個節點的 next 就是要刪除的那個節點 delNode,
    8. 而後讓prev.next = delNode.next
    9. 這樣就讓待刪除的那個節點的前一個節點的 next,
    10. 也就是直接指向了待刪除的那個節點的後一個節點了,
    11. 而後讓delNode.next = null 就完成了刪除,
    12. 千萬不要這樣delNode = delNode.next
    13. 這個並非刪除,而是將待刪除節點的引用指向了下一個節點,
    14. 經過設置爲 null 纔是真正的與當前鏈表脫離關係。

代碼示例(class: MyLinkedList, class: Main)

  1. 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();
             }
       }
  2. 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);
             }
       }

鏈表的時間複雜度分析

  1. 增:O(n):在只對鏈表頭進行操做時爲O(1)
  2. 刪:O(n):在只對鏈表頭進行操做時爲O(1)
  3. 改:O(n)
  4. 查:O(n):只查鏈表頭的元素時爲O(1)
  5. 鏈表增刪改查的時間複雜度

    1. 總體上要比數組增刪改查的時間複雜度要差,
    2. 由於數組有一個優點,
    3. 你有索引的話你就能夠快速訪問,
    4. 而鏈表沒有這樣的優點
    5. 可是鏈表的優點是,
    6. 若是你只對鏈表頭進行增刪的操做,
    7. 那麼它的時間複雜度是 O(1)級別的,
    8. 並且它總體是動態的,因此不會大量的浪費內存空間。 6.鏈表適合作的事情
    9. 不去修改、也不去查任意的元素,
    10. 就算查,也只能查鏈表頭的元素,
    11. 查的時候,只有那樣纔是 O(1)級別的時間複雜度,
    12. 而且增長刪除的時候只對鏈表頭進行操做,
    13. 只有在這種時候纔會和數組總體的時間複雜度是同樣的,
    14. 不過鏈表總體是動態的,不會去大量的浪費內存空間,
    15. 因此它具備必定的優點。
  6. 鏈表還有諸多的改進的方式

    1. 因此應用鏈表在一些狀況下仍然是有它的優點的,
    2. 最最重要的是 鏈表自己是一種最最基礎的動態數據結構,
    3. 對這種動態數據結構要深入的理解和掌握,
    4. 對於學習更重要的動態數據結構是有巨大的幫助,
    5. 好比說二叉樹、平衡二叉樹、AVL、紅黑樹等等。

 添加操做 O(n)

  1. addLast(e)O(n)

    1. 會從頭遍歷到尾,
    2. 找到最後一個節點以後才能添加元素
  2. addFirst(e)O(1)

    1. 直接從頭部添加元素
  3. insert(index, e)O(n/2) = O(n)

    1. 會去遍歷鏈表中每個節點,
    2. 檢索到合適的位置纔會添加這個元素。

刪除操做 O(n)

  1. removeLast()O(n)

    1. 會去遍歷鏈表中每個節點,
    2. 找到最後一個節點後纔會刪除
  2. removeFirst()O(1)

    1. 直接從頭部刪除這個節點
  3. remove(index)O(n/2) = O(n)

    1. 會去遍歷鏈表中每個節點,
    2. 檢索到合適的位置纔會移除這個元素

修改操做 O(n)

  1. set(index, e)O(n)

    1. 會去遍歷鏈表中每個節點,
    2. 找到了合適位置的節點後,
    3. 纔會修改元素

查找操做 O(n)

  1. get(index)O(n)

    1. 會去遍歷鏈表中每個節點,
    2. 找到了合適位置的節點後,
    3. 返回這個元素。
  2. contains(e)O(n)

    1. 會去遍歷鏈表中每個節點,
    2. 返回 是否有相同元素的 bool 值。
  3. find(e)O(n)

    1. 這個方法對鏈表來講是沒有用的,
    2. 就算你拿到了那個索引你也不能快速訪問。

使用鏈表來實現棧

  1. 對鏈表進行添加操做時

    1. 時間複雜度爲O(n)
    2. 可是在只對鏈表頭進行操做時爲O(1)
  2. 對鏈表進行刪除操做時

    1. 時間複雜度爲O(n)
    2. 可是在只對鏈表頭進行操做時爲O(1)
  3. 對鏈表進行查詢操做時

    1. 時間複雜度爲O(n)
    2. 可是在只查鏈表頭的元素時爲O(1)
  4. 這些特性很符合棧的需求

    1. 棧是後進先出的,而且棧只查棧頂的元素,
    2. 因此可使用鏈表來實現棧這樣的數據結構。

鏈表實現的棧

  1. 首先定義接口IMyLinkedListStack

    1. 而後讓MyLinkedListStack來實現這些接口。
  2. IMyLinkedListStack

    1. void push(E e):添加一個元素
    2. E pop():移除一個元素
    3. E peek():查看棧頂的元素
    4. int getSize():獲取棧中實際的元素個數
    5. boolean isEmpty():判斷棧是否爲空

代碼示例

  1. (Interface: IMyLinkedListStack, class: MyLinkedList,

    1. class: MyLinkedListStack, class: Main)
  2. 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.");
        }
  }
相關文章
相關標籤/搜索