这篇文章最初发表在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/