# 用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
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.