# 用Java學資料結構 - 雙向鏈結串列 Doubly Linked List ## 介紹與概念 ### 雙向鏈結串列的定義 雙向鏈結串列 (Doubly Linked List) 是一種進階的鏈結串列,由節點 (Node) 組成,每個節點包含資料 (data)、指向下一個節點的指標 (next) 和指向前一個節點的指標 (prev)。透過這些指標,可以輕鬆地在正向和反向之間遍歷串列。 ### 和單向鏈結串列的差異比較 |特性 |單向鏈結串列 |雙向鏈結串列| |------|------------|------| |節點結構 |包含資料與下一個節點的指標| 包含資料、前一個與下一個節點的指標| |插入/刪除效率| 相對簡單| 需要更新前後節點的指標| |記憶體使用| 只需額外儲存一個指標| 需額外儲存兩個指標| |遍歷方向 |僅能單向遍歷 |可正向與反向遍歷| 雙向鏈結串列雖然需要更多的記憶體來儲存指標,但它的雙向遍歷特性,使得操作更加靈活,適合某些需要快速插入、刪除以及雙向遍歷的應用場景。 ## 使用場景與優缺點 - ✅ 雙向遍歷 - 雙向鏈結串列的最大優勢是可以從頭到尾,也可以從尾到頭遍歷,非常適合需要雙向操作的場景: - 實際應用 - 音樂播放器的播放列表:支持上一首與下一首歌曲切換。 - 編輯器的撤銷與重做功能:需要追蹤前後狀態。 -✅ 插入與刪除效率高 - 和單向鏈結串列一樣,雙向鏈結串列在插入與刪除操作上也非常高效,因為只需要調整相關指標即可。 - ❌ 記憶體開銷 - 雙向鏈結串列需要額外的記憶體來儲存前一個節點的指標,因此在記憶體使用效率上不如單向鏈結串列與陣列。 ## 結構與術語 ### 節點 (Node) 的概念 雙向鏈結串列的節點由以下三部分組成: - 資料 (data):存放節點的值或資訊。 - 前指標 (prev):指向前一個節點的指標。 - 後指標 (next):指向下一個節點的指標。 ```java class Node { int data; Node prev; Node next; public Node(int data) { this.data = data; this.prev = null; this.next = null; } } ``` ### 頭節點 (Head) 與尾節點 (Tail) 頭節點 (Head):雙向鏈結串列的起始節點,prev 指標為 null。 尾節點 (Tail):雙向鏈結串列的結尾節點,next 指標為 null。 ### 實作與程式碼範例 雙向鏈結串列 (Doubly Linked List) 的實作 ```java class DoublyLinkedList { private Node head; private Node tail; public DoublyLinkedList() { this.head = null; this.tail = null; } // 新增節點到頭部 public void addFirst(int data) { Node newNode = new Node(data); if (head == null) { head = tail = newNode; } else { newNode.next = head; head.prev = newNode; head = newNode; } } // 新增節點到尾部 public void addLast(int data) { Node newNode = new Node(data); if (tail == null) { head = tail = newNode; } else { newNode.prev = tail; tail.next = newNode; tail = newNode; } } // 插入節點到指定位置 public void insert(int index, int data) { if (index < 0) { return; } if (index == 0) { addFirst(data); return; } Node current = head; for (int i = 0; i < index - 1 && current != null; i++) { current = current.next; } if (current == null) { return; } Node newNode = new Node(data); newNode.next = current.next; if (current.next != null) { current.next.prev = newNode; } else { tail = newNode; } newNode.prev = current; current.next = newNode; } // 刪除頭部節點 public void removeFirst() { if (head == null) { return; } if (head.next == null) { head = tail = null; } else { head = head.next; head.prev = null; } } // 刪除尾部節點 public void removeLast() { if (tail == null) { return; } if (tail.prev == null) { head = tail = null; } else { tail = tail.prev; tail.next = null; } } // 刪除指定位置的節點 public void remove(int index) { if (index < 0 || head == null) { return; } if (index == 0) { removeFirst(); return; } Node current = head; for (int i = 0; i < index && current != null; i++) { current = current.next; } if (current == null) { return; } if (current.next != null) { current.next.prev = current.prev; } else { tail = current.prev; } if (current.prev != null) { current.prev.next = current.next; } } // 反向輸出鏈結串列 public void printReverse() { Node current = tail; while (current != null) { System.out.print(current.data + " "); current = current.prev; } System.out.println(); } // 輸出鏈結串列 public void printList() { Node current = head; while (current != null) { System.out.print(current.data + " "); current = current.next; } System.out.println(); } } ``` ### 測試 ```java public class Main { public static void main(String[] args) { DoublyLinkedList list = new DoublyLinkedList(); list.addFirst(3); list.addFirst(2); list.addFirst(1); list.addLast(4); list.addLast(5); list.insert(2, 10); list.removeFirst(); list.removeLast(); list.remove(2); list.printList(); // 輸出: 2 3 4 list.printReverse(); // 輸出: 4 3 2 } } ``` ## 結論 雙向鏈結串列是單向鏈結串列的進階版本,適合需要雙向遍歷或頻繁插入/刪除的應用場景。雖然記憶體使用稍多,但帶來的靈活性在許多情況下是值得的。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up