Problem
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.
Follow up:
Can you solve it without using extra space?
Solution
Actually there is a famous algorithm for this: Floyd’s cycle detection algorithm, also known as the hare-tortoise algorithm.
Floyd算法用以判断是否有环且找到环的节点。参考 维基百科-Cycle detection 。算法如下:
0. 以快慢指针的方法先判断是否有环,令慢指针一次走一步,快指针一次走两步。假设链表有环,且快慢指针在某一点相遇。
1. 令链表起点到环起点的距离为m
2. 快慢指针相遇点到环起点的距离为k
3. 设环的长度为n,令a和b分别为慢指针和快指针相遇前在环内走的圈数。
4. 慢指针到相遇点的行走的距离为i = m + a*n + k,
5. 快指针到相遇点行走的距离为2i = m + b*n + k,
6. i = 2i - i = (b - a)*n 由这个公式可以表明i为环长度的倍数
7. 那么 i = (m + k) + a*n,m + k 等于环长度的倍数,也就是说快指针距离环起点的距离是m
8. 由 7 的公式可以得到,令慢指针一步一步从链表头部开始走,快指针从相遇点开始走,他们一定会在环起点相遇(走m步)。
由上算法得到代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
bool is_circle = false;
ListNode* slow = head;
ListNode* fast = head;
while(slow != NULL && fast != NULL){
slow = slow->next;
if(fast->next == NULL){
break;
}else{
fast = fast->next->next;
}
if(slow == fast){
is_circle = true;
break;
}
}
if(is_circle){
slow = head;
while(slow != fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}else{
return NULL;
}
}
};
当m=0时,即系链表的起点为环的起点时,即系 i = m + a*n + k = a*n + k, k为n的倍数,就是说明相遇点就是环的起点。
More
如何判断环的长度?判断链表有环后,快指针不动,慢指针从环相遇点开始再走一圈直到回到相遇点,所得步数即系环的长度。