Linked list (鏈結串列) | 資料結構&演算法
 
        
        
        資料結構 鏈結串列(Linked List) 是一種基本的資料結構,用於存儲一系列元素。在 JavaScript 中,可以使用物件來實現鏈結串列。鏈結串列由節點(Node)組成,每個節點包含一個數據元素和一個指向下一個節點的連結。
鏈結串列(Linked List) 在記憶體中並不是連續的保存
 
  Javascript中的陣列(Array),才是連續性的保存在同一個地方
 
鏈結串列(Linked List)的優勢,並且在特定的情況下是更適合使用的:
- 動態大小: 鏈結串列的大小可以動態調整,不像陣列(Array)需要事先指定大小。這使得鏈結串列更靈活,可以根據需要動態調整大小。
- 插入和刪除操作效率高: 在鏈結串列中插入和刪除節點的效率較高,特別是在頭部和尾部進行操作。這是因為只需要調整相應節點的指針,而不需要移動其他節點。
- 無須連續的記憶體空間: 相較於陣列,鏈結串列的節點在記憶體中可以分散存儲,不需要連續的記憶體空間。這有助於減少記憶體碎片化,尤其在動態環境中。
- 不需預先分配大小: 鏈結串列不需要事先指定大小,而陣列在創建時需要指定大小。這使得鏈結串列更容易應對動態數據。
- 簡單的插入和刪除操作: 在鏈結串列中,插入和刪除節點通常只需要改變相應節點的指針,而不需要移動其他節點。這在某些應用中可以提供更高效的操作。
鏈結串列適用於以下情況:
- 頻繁的插入和刪除操作: 鏈結串列在插入和刪除節點的效率上優於陣列,特別是在中間插入或刪除節點的情況下。
- 不確定數據大小: 如果不知道數據的大小,並且需要動態調整大小,鏈結串列是一個不錯的選擇。
- 不需要隨機訪問: 如果主要是需要順序訪問數據而不是根據索引隨機訪問,鏈結串列可以提供一個簡單且有效的解決方案。
以下是一個簡單的 JavaScript 鏈結串列(Linked List) 的實現:
- Node 類別:
- Node 類別用於創建鏈結串列的節點。每個節點包含一個值 (value) 和指向下一個節點的引用 (next)。
- LinkedList 類別:
- 建構子 (constructor):初始化一個鏈結串列,創建一個具有給定值的新節點,並將其設為 head 和 tail。初始長度為 1。
- printList 方法:列印鏈結串列的所有節點的值。
- getHead 方法:獲取並列印鏈結串列的頭部值。
- getTail 方法:獲取並列印鏈結串列的尾部值。
- getLength 方法:獲取並列印鏈結串列的長度。
- makeEmpty 方法:清空鏈結串列,將 head、tail 和長度都設為零。
- push 方法:在鏈結串列的尾部插入一個新節點。
- pop 方法:刪除鏈結串列的尾部節點。
- unshift 方法:在鏈結串列的頭部插入一個新節點。
- shift 方法:刪除鏈結串列的頭部節點。
- get 方法:根據索引獲取並返回相應位置的節點。
- set 方法:根據索引設置相應位置的節點的值。
- insert 方法:在指定索引處插入一個新節點。
- remove 方法:刪除指定索引處的節點。
- reverse 方法:反轉整個鏈結串列。
class Node {
  constructor(value){
    this.value = value;
    this.next = null;
  }
}
 
class LinkedList {
  constructor(value) {
    const newNode = new Node(value);
    this.head = newNode;
    this.tail = this.head;
    this.length = 1;
  }
  printList() {
    let temp = this.head;
    while (temp !== null) {
        console.log(temp.value);
        temp = temp.next;
    }
  }
  getHead() {
    if (this.head === null) {
        console.log("Head: null");
    } else {
        console.log("Head: " + this.head.value);
    }
  }
  getTail() {
    if (this.tail === null) {
        console.log("Tail: null");
    } else {
        console.log("Tail: " + this.tail.value);
    }
  }
  getLength() {
    console.log("Length: " + this.length);
  }
  makeEmpty() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }
  push(value) {
    const newNode = new Node(value);
    if (!this.head) {
        this.head = newNode;
        this.tail = newNode;
    } else {
        this.tail.next = newNode;
        this.tail = newNode;
    }
    this.length++;
    return this;
  }
  pop() {
    if (this.length === 0) return undefined;
    let temp = this.head;
    let pre = this.head;
    while (temp.next) {
        pre = temp;
        temp = temp.next;
    }
    this.tail = pre;
    this.tail.next = null;
    this.length--;
    if (this.length === 0) {
        this.head = null;
        this.tail = null;
    }
    return temp;
  }
  unshift(value) {
    const newNode = new Node(value);
    if (!this.head) {
        this.head = newNode;
        this.tail = newNode;
    } else {
        newNode.next = this.head;
        this.head = newNode;
    }
    this.length++;
    return this;
  }
  shift() {
    if (this.length === 0) return undefined;
    let temp = this.head;
    this.head = this.head.next;
    this.length--;
    if (this.length === 0) {
        this.tail = null;
    }
    temp.next = null;
    return temp;
  }
  get(index) {
    if (index < 0 || index >= this.length) return undefined;
    let temp = this.head;
    for (let i = 0; i < index; i++) {
        temp = temp.next;
    }
    return temp;
  }
  set(index, value) {
    let temp = this.get(index);
    if (temp) {
        temp.value = value;
        return true;
    }
    return false;
  }
  insert(index, value) {
    if (index < 0 || index > this.length) return false;
    if (index === this.length) return this.push(value);
    if (index === 0) return this.unshift(value);
    
    const newNode = new Node(value);
    const temp = this.get(index - 1);
    newNode.next = temp.next;
    temp.next = newNode;
    this.length++;
    return true;
  }
  remove(index) {
    if (index < 0 || index >= this.length) return undefined;
    if (index === 0) return this.shift();
    if (index === this.length - 1) return this.pop();
    const before = this.get(index - 1);
    const temp = before.next;
    before.next = temp.next;
    temp.next = null;
    this.length--;
    return temp;
  }
  reverse() {
    let temp = this.head;
    this.head = this.tail;
    this.tail = temp;
    let next = null;
    let prev = null;
    for (let i = 0; i < this.length; i++) {
      next = temp.next;
      temp.next = prev;
      prev = temp;
      temp = next;
    }
    return this;
  }
}
Linked List 跟 Array Operation 時間複雜度比較:
| 鏈結串列(Linked List) | 陣列(Array) | |
|---|---|---|
| Push | O(1) | O(1) | 
| Pop | O(n) | O(1) | 
| Shift | O(1) | O(n) | 
| Unshift | O(1) | O(n) | 
| Insert | O(n) | O(n) | 
| Delete | O(n) | O(n) | 
| Lookup by Index | O(n) | O(1) | 
| Lookup by Value | O(n) | O(n) | 
Tags