數據結構03 線性表之鏈表

上一篇總結完了順序表,這一篇要總結的是線性表之中的鏈表。我將會從如下幾點進行總結:html

一、爲何要使用鏈表? 
二、鏈表的存儲結構? 
三、鏈表的經常使用操做代碼實現?java

 

一、爲何要使用鏈表

經過上一篇的學習,咱們知道順序表存在一些問題,主要有如下兩個方面。node

一、順序表的長度是固定的,若是超出分配的長度就會形成溢出,若是存放的數據太少就會形成空間浪費。 
二、在插入元素和刪除元素時(尤爲插入和刪除的位置不在尾部時),會移動大量的元素,形成性能和效率低下。性能

基於以上問題,使用鏈表能夠很好地避免順序表中出現的問題。這也是咱們要使用鏈表的緣由。學習

 

二、鏈表的存儲結構

一、從上圖能夠看出,單鏈表中的每一個節點都包含一個「數據域」和一個「指針域」。「數據域」中包含當前節點的數據,「指針域」中包含下一節點的存儲地址。測試

二、頭指針 head 是指向開始節點的,結束節點沒有後繼節點,因此結束節點的指針域爲空,即 null。 this

三、鏈表在物理存儲結構上不連續,邏輯上連續;大小不固定。spa

四、根據鏈表的構造方式的不一樣能夠分爲:指針

  • 單向鏈表
  • 單向循環鏈表
  • 雙向鏈表
  • 雙向循環鏈表

2-一、單向鏈表

鏈表的每一個節點中只包含一個指針域,叫作單向鏈表(即構成鏈表的每一個節點只有一個指向後繼節點的指針)code

單向鏈表中每一個節點的結構:

2-二、單向循環鏈表

單向循環鏈表和上面講的單向鏈表有點類似,都是經過節點的指針指向它的下一個節點,而後這樣鏈接起來,但不一樣的地方是單向鏈表的最後一個節點的指針爲null,而單向循環鏈表的最後一個節點的指針指向的是頭節點,這樣構成了一個循環的節點的環。下面是單向循環鏈表的示意圖:

2-三、雙向鏈表

聽名字可能就能猜到雙向鏈表就是鏈表的節點包含兩個指針,一個指針是指向它的下一個節點,另外一個指針指向它的上一個節點。這樣雙向鏈表就能夠經過第一個節點找到最後一個節點,也能夠經過最後一個節點找到第一個節點,雙向鏈表的示意圖:

2-四、雙向循環鏈表

雙向循環鏈表相對於上面的雙向鏈表多了「循環」兩個字,咱們就能夠結合單向循環鏈表聯想到,其實雙向循環鏈表就是雙向鏈表夠成了一個環。雙向循環鏈表的示意圖:

 

三、鏈表的經常使用操做

鏈表經常使用的操做有:(以單向鏈表爲例)

3-一、插入節點

思路:須要循環查找此節點應該插入的位置。因此時間複雜度爲O(n)

示意圖:

3-二、刪除節點

思路:循環查找要刪除的節點的位置,因此時間複雜度爲O(n)

示意圖:

3-三、查找節點

思路:與插入節點和刪除節點的方法相似,須要遍歷鏈表中的節點,因此時間複雜度爲O(n)

 

3-四、獲取鏈表的長度

思路:不像順序表那樣連續存儲,獲取表的長度很是容易;在鏈表中,數據不是連續存儲的,所以須要循環遍歷才能求得鏈表的長度,因此時間複雜度爲O(n) 

 

四、鏈表的經常使用操做的代碼實現

4-一、插入節點

在代碼裏面用到了一個變量 position,它的含義以下圖所示:

注意:

一、頭節點不存儲數據,它的數據域爲null,它的地址域存儲了第1個節點的地址

二、頭節點不算進鏈表的長度,position 從頭節點後面的節點開始算起

三、每一個節點裏面分別有數據域和地址域  

 

下面是具體的實現代碼:

先建立一個節點類: Node.java

package com.demo; /** * 節點類 */
public class Node { Object element; // 數據域
    Node next; // 地址域 // 節點的構造方法
    public Node(Object element, Node next) { this.element = element; this.next = next; } // Gettet and Setter
    public Node getNext() { return this.next; } public void setNext(Node next) { this.next = next; } public Object getElement() { return this.element; } public void setElement(Object element) { this.element = element; } }

注意每一個節點裏面分別有數據域和地址域! 

 

定義鏈表類:MyLinkedList.java

package com.demo; /** * 本身定義的一個鏈表類 */
public class MyLinkedList { // 頭節點
    private Node headNode; // 用來遍歷鏈表的臨時節點
    private Node tempNode; // 鏈表的初始化方法
    public MyLinkedList() { // 初始化時,鏈表裏面只有1個節點,因此這個節點的地址域爲null
        Node node = new Node("王重陽", null); // 頭節點不存儲數據,它的數據域爲null,它的地址域存儲了第1個節點的地址
        headNode = new Node(null, node); } /** * 一、插入節點:時間複雜度爲O(n) * @param newNode 須要插入的新節點 * @param position 這個變量的範圍能夠從0到鏈表的長度,注意不要越界。 * 頭節點不算進鏈表的長度, * 因此從頭節點後面的節點開始算起,position爲0 */
    public void insert(Node newNode, int position) { // 經過position變量,讓tempNode節點從頭節點開始遍歷,移動到要插入位置的前一個位置
        tempNode = headNode; int i = 0; while (i < position) { tempNode = tempNode.next; i++; } newNode.next = tempNode.next; tempNode.next = newNode; } // 遍歷鏈表,打印出全部節點的方法
    public void showAll() { tempNode = headNode.next; while (tempNode.next != null) { System.out.println(tempNode.element); tempNode = tempNode.next; } System.out.println(tempNode.element); } }

測試類:TestMyLinkedList.java

package com.demo; public class TestMyLinkedList { public static void main(String[] args) { MyLinkedList myLinkedList = new MyLinkedList(); Node newNode1 = new Node("歐陽鋒", null); Node newNode2 = new Node("黃藥師", null); Node newNode3 = new Node("洪七公", null); myLinkedList.insert(newNode1, 0); myLinkedList.insert(newNode2, 0); myLinkedList.insert(newNode3, 0); myLinkedList.showAll(); } }

 

運行結果:

王重陽是初始化的節點,以後又往鏈表裏插入了3個節點。注意它們插入的位置和打印出來的順序!

 

4-二、刪除節點

MyLinkedList.java 中添加刪除節點的方法

/** * 二、刪除節點:時間複雜度爲O(n) * @param position */
    public void delete(int position) { // 這裏一樣須要用tempNode從頭開始向後查找position
        tempNode = headNode; int i = 0; while (i < position) { tempNode = tempNode.next; i++; } tempNode.next = tempNode.next.next; }

 

測試代碼:TestMyLinkedList.java

package com.demo; public class TestMyLinkedList { public static void main(String[] args) { MyLinkedList myLinkedList = new MyLinkedList(); Node newNode1 = new Node("歐陽鋒", null); Node newNode2 = new Node("黃藥師", null); Node newNode3 = new Node("洪七公", null); myLinkedList.insert(newNode1, 0); myLinkedList.insert(newNode2, 0); myLinkedList.insert(newNode3, 0); myLinkedList.showAll(); myLinkedList.delete(0); System.out.println("------刪除以後------"); myLinkedList.showAll(); } }

 

運行結果:

 

4-三、查找節點

在 MyLinkedList.java 中添加查找節點的方法

/** * 三、查找節點:時間複雜度爲O(n) * @param position * @return 返回查找的節點 */
    public Node find(int position) { // 這裏一樣須要用tempNode從頭開始向後查找position
        tempNode = headNode; int i = 0; while (i < position) { tempNode = tempNode.next; i++; } return tempNode.next; }

 

測試代碼:TestMyLinkedList.java

package com.demo; public class TestMyLinkedList { public static void main(String[] args) { MyLinkedList myLinkedList = new MyLinkedList(); Node newNode1 = new Node("歐陽鋒", null); Node newNode2 = new Node("黃藥師", null); Node newNode3 = new Node("洪七公", null); myLinkedList.insert(newNode1, 0); myLinkedList.insert(newNode2, 0); myLinkedList.insert(newNode3, 0); myLinkedList.showAll(); System.out.println("----查找position爲2的節點----"); Node result = myLinkedList.find(2); System.out.println(result.element); } }

 

運行結果:

 

4-四、獲取鏈表的長度

在 MyLinkedList.java 中添加方法

/** * 四、獲取鏈表的長度:時間複雜度爲O(n) * @return
     */
    public int size() { tempNode = headNode.next; int size = 0; while (tempNode.next != null) { size = size + 1; tempNode = tempNode.next; } size = size + 1; // tempNode的地址域爲null時,size記得加上最後一個節點
        return size; }

 

測試代碼:TestMyLinkedList.java

package com.demo; public class TestMyLinkedList { public static void main(String[] args) { MyLinkedList myLinkedList = new MyLinkedList(); Node newNode1 = new Node("歐陽鋒", null); Node newNode2 = new Node("黃藥師", null); Node newNode3 = new Node("洪七公", null); myLinkedList.insert(newNode1, 0); myLinkedList.insert(newNode2, 0); myLinkedList.insert(newNode3, 0); myLinkedList.showAll(); System.out.println("----------"); System.out.println("鏈表的長度爲:" + myLinkedList.size()); } }

 

運行結果:

 

五、總結 

一、鏈表插入和刪除一個元素的時間複雜度均爲O(n),另外,鏈表讀取一個數據元素的時間複雜度也爲O(n) 

二、順序表和單鏈表的比較:

①順序表:

  優勢:主要優勢是讀取元素的速度較快,以及內存空間利用效率高;

  缺點:主要缺點是須要預先給出順序表的最大元素個數,而這一般很難準確作到。當實際的元素個數超過了預先給出的個數,會發生異常。另外,順序表插入和刪除操做時須要移動較多的數據元素

②單鏈表:

  優勢:主要優勢是不須要預先給出最大元素個數。另外,單鏈表插入和刪除操做時不須要移動數據元素

  缺點:主要缺點是每一個節點都須要分爲地址域和數據域,所以單鏈表的空間利用率略低於順序表。另外,單鏈表讀取一個元素的時間複雜度爲O(n)  ;而順序表讀取一個元素的時間複雜度爲O(1)  

 

歡迎轉載,但請保留文章原始出處

本文地址:http://www.cnblogs.com/nnngu/p/8252589.html 

相關文章
相關標籤/搜索