目录
我们先来观察一个带环链表
我们可以换个更简单的方式来观察它
我们可以看到带环链表的循环方式。
1. 判断链表是否带环
我们可以尝试,使用快慢指针来判断,链表是否带环。
如果有环,快指针先一步进环,在环内循环。等到慢指针进环后,快指针与慢指针相遇,判断若相等,则链表带环。
如果不带环,快指针走到尾就结束了。
大致的思路就是这样。
大致的思路有了,接下来我们需要考虑一些细节问题,
1.1 快指针一次走几步,慢指针一次走几步
我们怎么确定,快慢指针一定在环中相遇?
先说结论:
快指针一次走俩步,慢指针一次走一步。
假设慢指针进环时 ,它们的差距为 N, 快指针走两步,慢指针走一步,它们的差距减少 1,
即 N- 1 , N -2 , N-3 , N-4 , …… 0 它们在0处相遇。
如果不按上面的 ,快指针一次走三步,四步,…… 可以吗
接下来我们以 快指针一次走三步,慢指针一次走一步为例
假设慢指针进环时,它们的差距为 N, 它们的差距一次减少2
N - 2
N -4
N- 6
N- 8
如果N为奇数
1
-1
(跳过了 0,它们没有相遇 ,fast 直接超过了 slow )
假设环的长度为 C ,那么此时它们的差距为 C-1 ,如果 ,C-1 为奇数 ,那么接着循环的后果就是,它们的距离最小任然为 -1 ,fast 用于也遇不到slow 。
总结一下 ,如果进环时,它们的差距N为奇数 ,且环长度C -1,也为奇数时,它们永远不会相遇。
所以我们在设置快慢指针时,为了避免这种情况,通常设置快指针一次走两步。
2. 求入环节点
在快慢指针相遇时,fast 可能已经在环内循环了很多圈,假设从起点到入口点距离为 x,入口点到相遇点为 Y ,环长度为 C,fast 循环了 n 次。
慢指针走的路程是 X + Y
快指针走的路程是 X + n*C +Y
由于快指针走的路程是慢指针走的二倍
2(X+Y) = X+ n*C +Y
X = n*C -Y
此时,一个指针从 head 处出发,一个指针从 meet 处出发,它们会在 入口处相遇。
代码实例:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode*slow = head,*fast = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow) // 相遇
{
struct ListNode* meet = fast;
while(head != meet)
{
head = head->next;
meet = meet->next;
}
return meet;
}
}
return NULL;
}