連結串列(下):如何輕鬆寫出正確的連結串列程式碼?
本文是學習演算法的筆記,《資料結構與演算法之美》,極客時間的課程
寫好連結串列程式碼
技巧一、理解指標或引用的含義
不管是C語言中指標的含義,還是JAVA語言中引用的概念,它們的意思是一樣的,都是儲存所指物件的記憶體地址。
編寫連結串列程式碼的時候,這樣的程式碼: p -> next=q。意思是p結點的next指標儲存了q結點的記憶體地址。
還有更復雜的 p -> next = p -> next -> next。這個意思是p結點next指標儲存了p結點下下琴結點的記憶體地址。
技巧二、警惕指標丟失和記憶體洩漏
p -> next = x; // 將 p 的 next 指標指向 x 結點;
x ->next = p -> next // 將 x 的結點的 next 指標指向 b 結點;
對於C 語言來說,這樣會出現指標丟失,記憶體洩漏(x 結點的 next 指標指向了自己)
對於剛剛的插入程式碼,只需要把第1行和第2行順序顛倒一下就可以了。
技巧三、利用哨兵簡化實現難度
在連結串列的p結點後面插入一個新的節點,只需要兩行程式碼就可以了
new_code -> next = p -> next;
p -> next = new_code;
當連結串列是空的,第一個節點插入是特殊的
if(head == null){
head = new_code;
}
類似的,刪除一個節點
if(head -> next == null){
head = null;
}
針對連結串列的刪除和插入操作,需要考慮第一個節點和最後一個節點的特殊情況,實現起來會有繁瑣,有沒有簡潔的方法呢?
技巧三、引入哨兵
引入哨兵之後,不管連結串列是不是空連結串列,head指標都會一起指向哨兵節點。我們把這種連結串列叫做帶頭連結串列。同樣,沒有哨兵的叫不帶頭連結串列。
如圖,有了哨兵節點後,插入刪除操作可以用統一的程式碼實現了。
技巧四、留意邊界的處理
比如連結串列為空、比如只包含一個結點或兩個節點的情況。比如處理頭結點和尾節點時,程式碼是否正確.
再就是 多寫多練了,這裡給出java語言實現的連結串列程式碼(這是我從課程的文字中複製出來的)
public class LinkedListDemo{ // 單鏈表反轉 public static Node reverse(Node list) { Node headNode = null; Node previousNode = null; Node currentNode = list; while (currentNode != null) { Node nextNode = currentNode.next; if (nextNode == null) { headNode = currentNode; } currentNode.next = previousNode; previousNode = currentNode; currentNode = nextNode; } return headNode; } // 檢測環 public static boolean checkCircle(Node list) { if (list == null) return false; Node fast = list.next; Node slow = list; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; if (slow == fast) return true; } return false; } // 有序連結串列合併 public static Node mergeSortedLists(Node la, Node lb) { if (la == null) return lb; if (lb == null) return la; Node p = la; Node q = lb; Node head; if (p.data < q.data) { head = p; p = p.next; } else { head = q; q = q.next; } Node r = head; while (p != null && q != null) { if (p.data < q.data) { r.next = p; p = p.next; } else { r.next = q; q = q.next; } r = r.next; } if (p != null) { r.next = p; } else { r.next = q; } return head; } // 刪除倒數第K個結點 public static Node deleteLastKth(Node list, int k) { Node fast = list; int i = 1; while (fast != null && i < k) { fast = fast.next; ++i; } if (fast == null) return list; Node slow = list; Node prev = null; while (fast.next != null) { fast = fast.next; prev = slow; slow = slow.next; } if (prev == null) { list = list.next; } else { prev.next = prev.next.next; } return list; } // 求中間結點 public static Node findMiddleNode(Node list) { if (list == null) return null; Node fast = list; Node slow = list; while (fast.next != null && fast.next.next != null) { fast = fast.next.next; slow = slow.next; } return slow; } public static void printAll(Node list) { Node p = list; while (p != null) { System.out.print(p.data + " "); p = p.next; } System.out.println(); } public static Node createNode(int value) { return new Node(value, null); } public static class Node { private int data; private Node next; public Node(int data, Node next) { this.data = data; this.next = next; } public int getData() { return data; } } }