1. 程式人生 > >連結串列(下):如何輕鬆寫出正確的連結串列程式碼?

連結串列(下):如何輕鬆寫出正確的連結串列程式碼?

本文是學習演算法的筆記,《資料結構與演算法之美》,極客時間的課程

寫好連結串列程式碼

技巧一、理解指標或引用的含義

不管是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;
		}
	}
}