[ JavaScript ] 資料結構與演算法 —— 連結串列

yhtx1997發表於2019-02-19

本篇主要有三部分

  • 什麼是連結串列
  • 連結串列的實現
  • 連結串列的變種

原始碼地址:github.com/yhtx1997/Sm…

另外,今天2019年2月18日上午發現 2048-vue 版,程式碼版本不對,且最新版本遺失,無奈只得重新修復了下
2048-vue地址: github.com/yhtx1997/Sm…

什麼是連結串列

連結串列儲存有序的元素集合,但不同於陣列,連結串列中的元素在記憶體中並不是連續放置的。每個 元素由一個儲存元素本身的節點和一個指向下一個元素的引用(也稱指標或連結)組成。
相對於傳統的陣列,連結串列的一個好處在於,新增或移除元素的時候不需要移動其他元素。然 而,連結串列需要使用指標,因此實現連結串列時需要額外注意。陣列的另一個細節是可以直接訪問任何 位置的任何元素,而要想訪問連結串列中間的一個元素,需要從起點(表頭)開始迭代列表直到找到 所需的元素。
如下圖:

1.png

注:其中 00 06 10 12 18 為假定在記憶體中的地址

我將已經做好的連結串列存入資料,然後在控制檯列印出來是這樣的:

TIM截圖20190218164943.png

它看起來就像是這樣的,一層套一層

0.png

其實應該是下面這樣,類似於栓狗的鐵鏈

3.png

連結串列的實現

連結串列功能

  • 新增元素
  • 獲取指定位置元素
  • 在指定位置插入元素
  • 移除指定位置的元素
  • 返回指定元素的位置
  • 移除指定元素
  • 是否為空
  • 長度
  • 獲取表頭
  • 清空連結串列
  • 轉換為字串輸出
// 連結串列元素
class Node {
    constructor(element) {
        this.element = element; // 元素
        this.next = undefined; // 指向下一個元素
    }
}
class LinkedList {
    // 建構函式宣告一些全域性變數
    constructor(){
        this.count = 0; // 長度
        this.head = undefined; // 第一個元素
    }
    // 新增元素
    push(element) {
        
    }
    // 獲取指定位置元素
    getElementAt(index) {
        
    }
    // 在指定位置插入元素
    insert(element, index) {
        
    }
    // 移除指定位置的元素
    removeAt(index) {
    
    }
    // 返回指定元素的位置
    indexOf(element) {
        
    }
    // 移除指定元素
    remove(element) {
        
    }
    // 是否為空
    isEmpty() {
        
    }
    // 長度
    size() {
        
    }
    // 獲取表頭
    getHead() {
       
    }
    // 清空連結串列
    clear() {
        
    }
    // 轉換為字串輸出
    toString() {
        
    }
}
複製程式碼

程式碼實現

class LinkedList {
    // 建構函式宣告一些全域性變數
    constructor(){
        this.count = 0; // 長度
        this.head = undefined; // 第一個元素
    }
    // 新增元素
    push(element) {
        const node = new Node(element);
        if (this.head === undefined) {
            this.head = node;
        } else {
            let current = this.head;
            while (current.next !== undefined) {
                current = current.next;
            }
            current.next = node;
        }
        this.count++;
    }
    // 獲取指定位置元素
    getElementAt(index) {
        // 判斷不是空連結串列
        if (this.isEmpty() || index > this.count || index < 0) { 
            // 非空才能繼續處理
            // 判斷不大於最大長度,不小於最小長度(0)
            return undefined;
        }
        // 迴圈找到元素
        let current = this.head;
        for (let i = 0; i < index; i++){
            current = current.next;
        }
        return current;// 返回找到的元素
    }
    // 在指定位置插入元素
    insert(element, index) {
        // 建立一個元素
        let current = new Node(element);
        // 首先確定是不是在首位置插入
        if (index === 0){
            current.next = this.head;
            this.head = current;
        } else {
            // 找到指定位置前一個元素
            let previous = this.getElementAt(index - 1);
            // 將前一個元素的 next 賦值給插入元素的 next
            current.next = previous.next;
            // 將插入元素的 node 賦值給前一個元素的 next
            previous.next = current;
        }
        this.count++;
    }
    // 移除指定位置的元素
    removeAt(index) {
        let current = this.head;
        if (index === 0){
            this.head = current.next;
        } else {
            // 找到這個元素和這個元素之前的元素
            let previous = this.getElementAt(index - 1);
            current = previous.next;
            // 將這個元素的 next 賦值給這個元素之前元素的 next
            previous.next = current.next;
        }
        this.count--;
        // 返回要移除的元素
        return current.element;
    }
    // 返回指定元素的位置
    indexOf(element) {
        // 從頭開始找
        let current = this.head;
        // 不超過最大長度
        for (let i = 0; i < this.size() && current != null; i++){
            if (current.element === element){ // 找到相等的就返回下標
                return i;
            }
            current = current.next;
        }
        return -1;
    }
    // 移除指定元素
    remove(element) {
        // 獲取指定元素位置
        let index = this.indexOf(element);
        // 移除指定位置元素
        return this.removeAt(index);
    }
    // 是否為空
    isEmpty() {
        return this.size() === 0;
    }
    // 長度
    size() {
        return this.count;
    }
    // 獲取表頭
    getHead() {
        return this.head;
    }
    // 清空連結串列
    clear() {
        this.head = undefined;
        this.count = 0;
    }
    // 轉換為字串輸出
    toString() {
        if (this.head == null) {
            return '';
        }
        let objString = `${this.head.element}`;
        let current = this.head.next;
        for (let i = 1; i < this.size() && current != null; i++) {
            objString = `${objString},${current.element}`;
            current = current.next;
        }
        return objString;
    }
}
let a = new LinkedList();
a.push('a');
a.push('b');
a.push('c');
a.push('d');
a.push('e');
a.push('f');
a.push('h');
a.push('i');
a.push('j');
a.push('k');
a.push('l');
a.push('m');
a.push('n');
a.push('o');
a.push('p');
a.push('q');
a.remove('a');
a.insert('a',1);
console.log(a);
複製程式碼

插入元素圖解:

現在有狗鏈兩節,我要在中間加一節

4.png

先把兩節分開,

5.png

然後把前邊的尾部與要加的頭部相連,然後把要加的尾部與後邊的頭部相連

0 連 xx , xx 連 1

6.png

連結串列的變種

雙向連結串列

我們已經知道連結串列的每個元素由一個儲存元素本身的節點和一個指向下一個元素的引用(也稱指標或連結)組成,雙向連結串列除了這個基本特性,每個元素還包含一個指向前一個元素的引用,如圖所示:

TIM截圖20190219171236.png

迴圈連結串列

迴圈連結串列就是連結串列的最後一個指向下一個元素的引用指向了第一個元素,使其成為迴圈連結串列

雙向迴圈連結串列

雙向迴圈連結串列就是雙向連結串列的第一個元素指向前一個的引用指向了最後一個元素,而最後一個元素指向下一個元素的引用指向了第一個元素,如圖所示:

TIM截圖20190219171514.png

相關文章