Java版-資料結構-連結串列

小白程式之路發表於2019-03-30

概要

之前我們分別學習瞭解了動態陣列、棧、佇列,其實他們的底層都是依託靜態陣列來實現的、只是通過我們定義的resize方法來動態擴容解決固定容量的問題,那麼我們即將學習的連結串列,它其實是一種真正的動態資料結構。

介紹

連結串列是一種最簡單的動態資料結構,它能夠輔助組成其它的資料結構,連結串列中的元素可儲存在記憶體中的任何地方(不需要連續的記憶體,這一點和我們的陣列具有很大的區別,陣列需要連續的記憶體),連結串列中的每個元素都儲存了下一個元素的地址,從而使一系列隨機的記憶體地址串接在一起

儲存連結串列的資料的我們一般稱為節點(Node),節點一般分為兩部分,一部分儲存我們真正的資料,而另外一部分儲存的是下一個節點的引用地址。

class Node{
    private E e; // 儲存的真正元素
    private Node next;  // 儲存下一個node的引用地址(指向下一個node)
}
複製程式碼

比如現在我們將元素A、B、C三個節點新增到連結串列中,示意圖如下:

image-20190327213801994

從圖中節點A到節點B之間的箭頭代表,節點A指向了節點BNodeA.next = NodeB),因為在實際業務中我們的連結串列長度不可能是無窮無盡的,基本上都是有限個節點,通常定義連結串列中的最後一個元素它的next儲存的是NULL(空),換句話說,如果在連結串列中,一個節點的next是空(NULL)的話,那麼它一定是最後一個節點(對應我們圖中的C節點)。

根據我們上面介紹的連結串列基本結構,下面我們用程式碼定義一下連結串列的基本骨架(這裡我們引入了一個虛擬的頭結點,下面我們會作說明)

public class LinkedList<E> {
    /**
     * 虛擬的頭結點
     */
    private Node dummyHead;
    /**
     * 連結串列中節點的個數
     */
    private int size;

    public LinkedList() {
        // 建立一個虛擬的頭結點
        dummyHead = new Node();
    }


    // 節點定義
    private class Node {
        // 儲存節點的元素
        public E e;
        // 儲存下一個節點的地址
        public Node next;

        public Node() {

        }

        public Node(E e) {
            this.e = e;
        }

        public Node(E e, Node next) {
            this.e = e;
            this.next = next;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "e=" + e +
                    ", next=" + next +
                    '}';
        }
    }
}
複製程式碼

後面我們對連結串列的新增節點、刪除節點以及查詢節點,程式碼實現都會基於這個基本骨架

向連結串列中新增節點

思路分析:

一般我們向連結串列中新增節點,基本思路:找到新增節點位置的前一個節點preNode,然後再改變連結串列的地址引用;由於連結串列的第一個節點也就是頭結點沒有前節點,此時我們為了操作方便,為連結串列新增了不儲存任何元素的一個虛擬的頭結點dummyHead(不是必須的,對使用者來講是不可見的),其實連結串列中真正的第一個節點是節點AdummyHead.next),這樣我們就能保證了,連結串列中儲存元素的節點都有前一個節點。

image-20190328144225867

下面我們來看一下,如果現在有一個節點D,我們準備把它插入到節點B的位置,我們需要做哪些操作

第一步:我們首先要找到節點B的前一個節點,也就是節點A

第二步:將新節點D的指向指到節點BNodeD.next = NodeA.next),然後再將節點A的指向,指到節點DNodeA.next = NodeD),這樣我們的節點就能串接起來了

image-20190330090951086

程式碼實現:
/**
  * 向連結串列中指定位置插入節點(學習使用,真正插入不會指定索引)
  *
  * @param index 索引的位置
  * @param e 節點元素
  */
public void add(int index, E e) {
    if (index < 0 || index > size) {
        throw new IllegalArgumentException("不是有效的索引");
    }

    Node prev = dummyHead;

    // 找到index位置的前一個節點
    for (int i = 0; i < index; i++) {
        prev = prev.next;
    }

    // 新建一個節點,進行掛接
    Node node = new Node(e);
    node.next = prev.next;
    prev.next = node;

    size++;
}
複製程式碼

連結串列的遍歷

進行連結串列遍歷,我們需要從連結串列中真正的第一個元素開始,也就是dummyHead.next

/**
  * 獲取連結串列中index位置的元素
  *
  * @param index  索引的位置
  * @return  節點的元素
  */
public E get(int index) {
    if (index < 0 || index > size) {
        throw new IllegalArgumentException("不是有效的索引");
    }
    Node cur = dummyHead.next;
    for (int i = 0; i < index; i++) {
        cur = cur.next;
    }
    return cur.e;
}
複製程式碼

修改連結串列中元素

/**
  * 修改連結串列中index位置節點的元素
  *
  * @param index 索引的位置
  * @param e 節點的元素
  */
public void set(int index, E e) {
    if (index < 0 || index > size) {
        throw new IllegalArgumentException("不是有效的索引");
    }
    Node cur = dummyHead.next;
    for (int i = 0; i < index; i++) {
        cur = cur.next;
    }
    cur.e = e;
}
複製程式碼

查詢連結串列中是否包含某元素

/**
  * 查詢連結串列中是否包含元素e
  *
  * @param e
  * @return
  */
public boolean contains(E e) {
    Node cur = dummyHead.next;
    while (cur != null) {
        if (cur.e.equals(e)) {
            return true;
        }
        cur = cur.next;
    }
    return false;
}
複製程式碼

刪除連結串列中的元素

image-20190330093907204

在連結串列中刪除元素,與在連結串列中新增元素有點類似

第一步:我們首先找到刪除節點位置的前一個節點,我們用prev表示,被刪除的節點我們用delNode表示

第二步:改變連結串列的引用地址:prev.next = delNode.next(等同於,將節點在連結串列中刪除)

/**
  * 刪除連結串列中index位置的節點
  *
  * @param index
  */
public void remove(int index) {
    if (index < 0 || index > size) {
        throw new IllegalArgumentException("不是有效的索引");
    }

    Node prev = dummyHead;

    for (int i = 0; i < index; i++) {
        prev = prev.next;
    }

    Node delNode = prev.next;
    prev.next = delNode.next;
    delNode.next = null;
    size--;
}
複製程式碼

完整版程式碼GitHub倉庫地址:Java版資料結構-連結串列 歡迎大家【關注】和【Star

至此筆者已經為大家帶來了資料結構:靜態陣列、動態陣列、棧、陣列佇列、迴圈佇列、連結串列;接下來,筆者還會一一的實現其它常見的陣列結構,大家一起加油!

  • 靜態陣列
  • 動態陣列
  • 陣列佇列
  • 迴圈佇列
  • 連結串列
  • 迴圈連結串列
  • 二分搜尋樹
  • 優先佇列
  • 線段樹
  • 字典樹
  • AVL
  • 紅黑樹
  • 雜湊表
  • ....

持續更新中,歡迎大家關注公眾號:小白程式之路(whiteontheroad),第一時間獲取最新資訊!!!

小白程式之路

筆者部落格地址:http:www.gulj.cn

相關文章