資料結構連結串列各種問題

TheAI發表於2018-01-06

一:連結串列原理

        連結串列是一種資料結構,和陣列同級。比如,Java中我們使用的ArrayList,其實現原理是陣列。而LinkedList的實現原理就是連結串列了。連結串列在進行迴圈遍歷時效率不高,但是插入和刪除時優勢明顯。下面對單向連結串列做一個介紹。

        單向連結串列是一種線性表,實際上是由節點(Node)組成的,一個連結串列擁有不定數量的節點。其資料在記憶體中儲存是不連續的,它儲存的資料分散在記憶體中,每個結點只能也只有它能知道下一個結點的儲存位置。由N各節點(Node)組成單向連結串列,每一個Node記錄本Node的資料及下一個Node。向外暴露的只有一個頭節點(Head),我們對連結串列的所有操作,都是直接或者間接地通過其頭節點來進行的。 


        上圖中最左邊的節點即為頭結點(Head),但是新增節點的順序是從右向左的,新增的新節點會被作為新節點。最先新增的節點對下一節點的引用可以為空。引用是引用下一個節點而非下一個節點的物件。因為有著不斷的引用,所以頭節點就可以操作所有節點了。 
        下圖描述了單向連結串列儲存情況。儲存是分散的,每一個節點只要記錄下一節點,就把所有資料串了起來,形成了一個單向連結串列。 


       節點(Node)是由一個需要儲存的物件及對下一個節點的引用組成的。也就是說,節點擁有兩個成員:儲存的物件、對下一個節點的引用。下面圖是具體的說明:


二、連結串列的實現

[html] view plain copy
  1. package com.zjn.LinkAndQueue;  
  2.   
  3. /**  
  4.  * 自定義連結串列設計  
  5.  *   
  6.  * @author zjn  
  7.  *  
  8.  */  
  9. public class MyLink {  
  10.     Node head = null; // 頭節點  
  11.   
  12.     /**  
  13.      * 連結串列中的節點,data代表節點的值,next是指向下一個節點的引用  
  14.      *   
  15.      * @author zjn  
  16.      *  
  17.      */  
  18.     class Node {  
  19.         Node next = null;// 節點的引用,指向下一個節點  
  20.         int data;// 節點的物件,即內容  
  21.   
  22.         public Node(int data) {  
  23.             this.data = data;  
  24.         }  
  25.     }  
  26.   
  27.     /**  
  28.      * 向連結串列中插入資料  
  29.      *   
  30.      * @param d  
  31.      */  
  32.     public void addNode(int d) {  
  33.         Node newNode = new Node(d);// 例項化一個節點  
  34.         if (head == null) {  
  35.             head = newNode;  
  36.             return;  
  37.         }  
  38.         Node tmp = head;  
  39.         while (tmp.next != null) {  
  40.             tmp = tmp.next;  
  41.         }  
  42.         tmp.next = newNode;  
  43.     }  
  44.   
  45.     /**  
  46.      *   
  47.      * @param index:刪除第index個節點  
  48.      * @return  
  49.      */  
  50.     public boolean deleteNode(int index) {  
  51.         if (index < 1 || index > length()) {  
  52.             return false;  
  53.         }  
  54.         if (index == 1) {  
  55.             head = head.next;  
  56.             return true;  
  57.         }  
  58.         int i = 1;  
  59.         Node preNode = head;  
  60.         Node curNode = preNode.next;  
  61.         while (curNode != null) {  
  62.             if (i == index) {  
  63.                 preNode.next = curNode.next;  
  64.                 return true;  
  65.             }  
  66.             preNode = curNode;  
  67.             curNode = curNode.next;  
  68.             i++;  
  69.         }  
  70.         return false;  
  71.     }  
  72.   
  73.     /**  
  74.      *   
  75.      * @return 返回節點長度  
  76.      */  
  77.     public int length() {  
  78.         int length = 0;  
  79.         Node tmp = head;  
  80.         while (tmp != null) {  
  81.             length++;  
  82.             tmp = tmp.next;  
  83.         }  
  84.         return length;  
  85.     }  
  86.   
  87.     /**  
  88.      * 在不知道頭指標的情況下刪除指定節點  
  89.      *   
  90.      * @param n  
  91.      * @return  
  92.      */  
  93.     public boolean deleteNode11(Node n) {  
  94.         if (n == null || n.next == null)  
  95.             return false;  
  96.         int tmp = n.data;  
  97.         n.data = n.next.data;  
  98.         n.next.data = tmp;  
  99.         n.next = n.next.next;  
  100.         System.out.println("刪除成功!");  
  101.         return true;  
  102.     }  
  103.   
  104.     public void printList() {  
  105.         Node tmp = head;  
  106.         while (tmp != null) {  
  107.             System.out.println(tmp.data);  
  108.             tmp = tmp.next;  
  109.         }  
  110.     }  
  111.   
  112.     public static void main(String[] args) {  
  113.         MyLink list = new MyLink();  
  114.         list.addNode(5);  
  115.         list.addNode(3);  
  116.         list.addNode(1);  
  117.         list.addNode(2);  
  118.         System.out.println("linkLength:" + list.length());  
  119.         System.out.println("head.data:" + list.head.data);  
  120.         list.printList();  
  121.         list.deleteNode(4);  
  122.         System.out.println("After deleteNode(4):");  
  123.         list.printList();  
  124.     }  
  125. }  
執行結果如下:

三、連結串列相關的常見面試題總結

 1. 連結串列反轉

調整指標的指向,反轉後連結串列的頭結點是原始連結串列的尾節點。

[html] view plain copy
  1. /**  
  2.      * 連結串列反轉  
  3.      *   
  4.      * @param head  
  5.      * @return  
  6.      */  
  7.     public Node ReverseIteratively(Node head) {  
  8.         Node pReversedHead = head;  
  9.         Node pNode = head;  
  10.         Node pPrev = null;  
  11.         while (pNode != null) {  
  12.             Node pNext = pNode.next;  
  13.             if (pNext == null) {  
  14.                 pReversedHead = pNode;  
  15.             }  
  16.             pNode.next = pPrev;  
  17.             pPrev = pNode;  
  18.             pNode = pNext;  
  19.         }  
  20.         this.head = pReversedHead;  
  21.         return this.head;  
  22.     }  
執行結果:


2.  查詢單連結串列的中間節點

    採用快慢指標的方式查詢單連結串列的中間節點,快指標一次走兩步,慢指標一次走一步,當快指標走完時,慢指標剛好到達中間節點。

[html] view plain copy
  1. /**  
  2.      * 連結串列反轉  
  3.      *   
  4.      * @param head  
  5.      * @return  
  6.      */  
  7.     public Node ReverseIteratively(Node head) {  
  8.         Node pReversedHead = head;  
  9.         Node pNode = head;  
  10.         Node pPrev = null;  
  11.         while (pNode != null) {  
  12.             Node pNext = pNode.next;  
  13.             if (pNext == null) {  
  14.                 pReversedHead = pNode;  
  15.             }  
  16.             pNode.next = pPrev;  
  17.             pPrev = pNode;  
  18.             pNode = pNext;  
  19.         }  
  20.         this.head = pReversedHead;  
  21.         return this.head;  
  22.     }  

3.查詢倒數第k個元素

        採用兩個指標P1,P2,P1先前移K步,然後P1、P2同時移動,當p1移動到尾部時,P2所指位置的元素即倒數第k個元素 。

[html] view plain copy
  1. /**  
  2.      * 查詢倒數 第k個元素  
  3.      *   
  4.      * @param head  
  5.      * @param k  
  6.      * @return  
  7.      */  
  8.     public Node findElem(Node head, int k) {  
  9.         if (k < 1 || k > this.length()) {  
  10.             return null;  
  11.         }  
  12.         Node p1 = head;  
  13.         Node p2 = head;  
  14.         for (int i = 0; i < k; i++)// 前移k步  
  15.             p1 = p1.next;  
  16.         while (p1 != null) {  
  17.             p1 = p1.next;  
  18.             p2 = p2.next;  
  19.         }  
  20.         return p2;  
  21.     }  

4. 對連結串列進行排序

[html] view plain copy
  1. /**  
  2.      * 排序  
  3.      *   
  4.      * @return  
  5.      */  
  6.     public Node orderList() {  
  7.         Node nextNode = null;  
  8.         int tmp = 0;  
  9.         Node curNode = head;  
  10.         while (curNode.next != null) {  
  11.             nextNode = curNode.next;  
  12.             while (nextNode != null) {  
  13.                 if (curNode.data > nextNode.data) {  
  14.                     tmp = curNode.data;  
  15.                     curNode.data = nextNode.data;  
  16.                     nextNode.data = tmp;  
  17.                 }  
  18.                 nextNode = nextNode.next;  
  19.             }  
  20.             curNode = curNode.next;  
  21.         }  
  22.         return head;  
  23.     }  

執行結果:

5. 刪除連結串列中的重複節點

[html] view plain copy
  1. /**  
  2.      * 刪除重複節點  
  3.      */  
  4.     public void deleteDuplecate(Node head) {  
  5.         Node p = head;  
  6.         while (p != null) {  
  7.             Node q = p;  
  8.             while (q.next != null) {  
  9.                 if (p.data == q.next.data) {  
  10.                     q.next = q.next.next;  
  11.                 } else  
  12.                     q = q.next;  
  13.             }  
  14.             p = p.next;  
  15.         }  
  16.   
  17.     }  

6.  從尾到頭輸出單連結串列,採用遞迴方式實現

[html] view plain copy
  1. /**  
  2.      * 從尾到頭輸出單連結串列,採用遞迴方式實現  
  3.      *   
  4.      * @param pListHead  
  5.      */  
  6.     public void printListReversely(Node pListHead) {  
  7.         if (pListHead != null) {  
  8.             printListReversely(pListHead.next);  
  9.             System.out.println("printListReversely:" + pListHead.data);  
  10.         }  
  11.     }  


7.  判斷連結串列是否有環,有環情況下找出環的入口節點

[html] view plain copy
  1. /**  
  2.      * 判斷連結串列是否有環,單向連結串列有環時,尾節點相同  
  3.      *   
  4.      * @param head  
  5.      * @return  
  6.      */  
  7.     public boolean IsLoop(Node head) {  
  8.         Node fast = headslow = head;  
  9.         if (fast == null) {  
  10.             return false;  
  11.         }  
  12.         while (fast != null && fast.next != null) {  
  13.             fast = fast.next.next;  
  14.             slow = slow.next;  
  15.             if (fast == slow) {  
  16.                 System.out.println("該連結串列有環");  
  17.                 return true;  
  18.             }  
  19.         }  
  20.         return !(fast == null || fast.next == null);  
  21.     }  
  22.   
  23.     /**  
  24.      * 找出連結串列環的入口  
  25.      *   
  26.      * @param head  
  27.      * @return  
  28.      */  
  29.     public Node FindLoopPort(Node head) {  
  30.         Node fast = headslow = head;  
  31.         while (fast != null && fast.next != null) {  
  32.             slow = slow.next;  
  33.             fast = fast.next.next;  
  34.             if (slow == fast)  
  35.                 break;  
  36.         }  
  37.         if (fast == null || fast.next == null)  
  38.             return null;  
  39.         slow = head;  
  40.         while (slow != fast) {  
  41.             slow = slow.next;  
  42.             fast = fast.next;  
  43.         }  
  44.         return slow;  
  45.     }  

相關文章