[Interview] 3. How to Detect a Cycle in a Linked List? How to Calculate the Cycle's Length?
-
How to detect a cycle? If you have two pointers, one moving fast and one moving slow, after a certain number of steps, the fast pointer will eventually overtake the slow pointer by one full cycle.
-
How to calculate the length of the cycle? Start counting when they meet for the first time (after the fast pointer has completed one cycle), and stop counting when they meet for the second time.
-
How to find the entry point of the cycle: The distance from the collision point (p) to the cycle's entry point is equal to the distance from the head pointer to the cycle's entry point. Therefore, if you start one pointer from the collision point and another from the head pointer, and both move one step at a time, the point where they meet will be the cycle's entry point.
Why? Here's a simple derivation:
(1) When fast and slow pointers meet, the slow pointer has definitely not traversed the entire linked list, while the fast pointer has completed n (n >= 1) cycles within the loop. Assume the slow pointer has moved s steps, then the fast pointer has moved 2s steps. The fast pointer's steps are also equal to the slow pointer's steps plus n full cycles within the loop, so we have:
2s = s + nr. Therefore, s = nr.
(2) Let the total length of the linked list be L, the distance from the entry point to the meeting point be X, and the distance from the start to the entry point be a. Since the slow pointer has not completed a full cycle:
a + x = s. Substituting the result from the first step, we get: a + x = nr = (n-1)r + r = (n-1)r + L - a; that is:
a = (n-1)r + L - a - x;
This shows that the distance from the head node to the entry point is equal to the distance from the meeting point to the entry point after (n-1) cycles. Therefore, we can place one pointer at the head of the list and another at the meeting point. By moving both pointers one step at a time, they are guaranteed to meet, and their first meeting point will be the entry point of the cycle.
Perhaps some of you might have a question: why is it that "when fast and slow pointers meet, the slow pointer has definitely not traversed the entire linked list"? This question is a bit tricky, and I haven't found a perfect proof for it. However, if you try drawing a few examples yourself, you'll find that it is indeed the case.
- How to determine if two (non-cyclic) linked lists intersect? Connect the tail of one linked list to its head, and then check if the other linked list contains a cycle. This is relatively simple, so the code is omitted.
[cpp] view plain copy
-
#include <stdio.h>
-
typedef struct Node
-
{
-
int val;
-
Node *next;
-
}Node,*pNode;
-
//判断是否有环
-
bool isLoop(pNode pHead)
-
{
-
pNode fast = pHead;
-
pNode slow = pHead;
-
//如果无环,则fast先走到终点
-
//当链表长度为奇数时,fast->Next为空
-
//当链表长度为偶数时,fast为空
-
while( fast != NULL && fast->next != NULL)
-
{
-
fast = fast->next->next;
-
slow = slow->next;
-
//如果有环,则fast会超过slow一圈
-
if(fast == slow)
-
{
-
break;
-
}
-
}
-
if(fast == NULL || fast->next == NULL )
-
return false;
-
else
-
return true;
-
}
-
//计算环的长度
-
int loopLength(pNode pHead)
-
{
-
if(isLoop(pHead) == false)
-
return 0;
-
pNode fast = pHead;
-
pNode slow = pHead;
-
int length = 0;
-
bool begin = false;
-
bool agian = false;
-
while( fast != NULL && fast->next != NULL)
-
{
-
fast = fast->next->next;
-
slow = slow->next;
-
//超两圈后停止计数,挑出循环
-
if(fast == slow && agian == true)
-
break;
-
//超一圈后开始计数
-
if(fast == slow && agian == false)
-
{
-
begin = true;
-
agian = true;
-
}
-
//计数
-
if(begin == true)
-
++length;
-
}
-
return length;
-
}
-
//求出环的入口点
-
Node* findLoopEntrance(pNode pHead)
-
{
-
pNode fast = pHead;
-
pNode slow = pHead;
-
while( fast != NULL && fast->next != NULL)
-
{
-
fast = fast->next->next;
-
slow = slow->next;
-
//如果有环,则fast会超过slow一圈
-
if(fast == slow)
-
{
-
break;
-
}
-
}
-
if(fast == NULL || fast->next == NULL)
-
return NULL;
-
slow = pHead;
-
while(slow != fast)
-
{
-
slow = slow->next;
-
fast = fast->next;
-
}
-
return slow;
-
}