题解-Linked List Cycle II

这篇文章最初发表在http://www.meetqun.com/thread-821-1-1.html ,此处略有改动。

建议想要认真准备的同学自己先尝试,看完分析尝试,然后把自己写的代码和我写的对比,看看有哪些地方做得比我好(比如我的单行if语句不套大括号,可能对代码修改的时候就不方便),哪些地方做得比我差(比如我用了fast,slow,而你用了没意义的命名),多总结,多反思。面试中代码写得如何也是考察的重中之重,我的感觉甚至比算法想没想出来还重要,毕竟算法工作之后基本不再需要,而面试官作为你未来的同事,可是要经常review你的代码的哟。总而言之,只是看水平是提升不了的。

题目

Leetcode: https://oj.leetcode.com/problems/linked-list-cycle-ii/

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Follow up:
Can you solve it without using extra space?

解法一:

1.1 分析

使用额外存储
用一个hashtable存储访问过的指针,若当前指针之前也被访问过,则为环入口点。

1.2 复杂度

时间O(n),空间O(n)

1.3 代码

ListNode *detectCycle(ListNode *head) {
	unordered_set<ListNode*> visited;
	while (head != nullptr) {
		if (visited.find(head) != visited.end()) 
							return head;
		visited.insert(head);
		head = head->next;
	}
	return nullptr;
}

解法二:

2.1 分析

使用快慢两个指针,快的指针一次走两步,慢的一次一步。若无环,快的会走到链表末尾。若有环,则快的先进入环内循环走圈,直到慢的进入之后,每同时走一次,快的就接近慢的一步,最终快的肯定会遇到慢的。
假设从链表到环入口点距离为K,环的长度为L。当慢的走到环入口点时,慢的前进了K,快的前进了2K,此时慢的距离快的K%L,快的距离快的L-K%L,再接着走L-K%L次,快的追上慢的,此时距离入口点L-K%L。也就是说,慢的再走K%L就又回到环入口点。

如果我们在快慢指针相遇时,让快的回到起始点,再跟慢的一起走,每次一步,那么快的走K步到环入口点,慢的此时也走了K步,由于相遇时慢的距离入口点K%L,所以此时慢的也在入口点,快的和慢的相遇在入口点!

2.2 复杂度

时间:O(n),空间O(1)

2.3 代码

ListNode *detectCycle(ListNode *head) {
	ListNode* fast = head;
	ListNode* slow = head;
	while (fast != nullptr && fast->next != nullptr) {
		fast = fast->next->next;
		slow = slow->next;
		if (fast == slow) break;
	}
	if (fast == nullptr || fast->next == nullptr)
		return nullptr;
	fast = head;
	while (slow != fast) {
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

解法三:

3.1 分析

同解法二,使用快慢两个指针,相遇时说明有环。如何找到环入口点呢?假如我们知道环的长度,我们可以设两个指针,让第一个指针先走环的长度这么多步,另一个指针指向链表头,然后两个指针一起走,那么慢的指针走到环入口点时,快的正好走过环入口点之后又走了环的长度,第二次到达环入口点,两个指针相遇在入口点!
那么如何获得环的长度呢?在检测是否有环的那个步骤中,快慢两个指针在环中相遇之后,继续每次快的走两步,慢的走一步,那么当它们再次相遇时,慢的正好走过一个环的长度。每走一次,快的接近慢的一步,可以认为开始的时候快的距离慢的环的长度那么多步。

3.2 复杂度

时间O(n),空间O(1)

3.3 代码

ListNode *detectCycle(ListNode *head) {
	ListNode* fast = head;
	ListNode* slow = head;
	while (fast != nullptr && fast->next != nullptr) {
		fast = fast->next->next;
		slow = slow->next;
		if (fast == slow) break;
	}
	if (fast == nullptr || fast->next == nullptr)
		return nullptr;
	int cycleLength = 0;
	do {
		slow = slow->next;
		fast = fast->next->next;
		++cycleLength;
	} while (slow != fast);
	fast = slow = head;
	while (cycleLength > 0) {
		fast = fast->next;
		--cycleLength;
	}
	while (fast != slow) {
		fast = fast->next;
		slow = slow->next;
	}
	return slow;
}

相关题目

Linked List Cycle:
https://leetcode.com/problems/linked-list-cycle/

原文地址:https://www.cnblogs.com/ridershen/p/4474672.html