链表(下):如何轻松写出正确的链表代码?

本文是学习算法的笔记,《数据结构与算法之美》,极客时间的课程

写好链表代码

技巧一、理解指针或引用的含义

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

猜你喜欢

转载自blog.csdn.net/every__day/article/details/83744905