题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/description/
题目大意:给一个链表的头部,判断链表是否有环,如果有,返回环的第一个指针;如果没有,返回nullptr
思路:简单的思路是并查集,第二次插入的那个指针就是环的起点。但这样空间复杂度还是 O ( N ) O(N) O(N)。使用快慢指针可以让空间复杂度降为 O ( 1 ) O(1) O(1)
快慢指针都从头部开始,slow
每次走1步,fast
每次走2步,如果链表有环,那么必然fast
会追上slow
。如果无环,某一次fast
的下一步就是null
。
判断很好完成,那么如何找到入环的点呢?假设环外长度为 a a a,环起点到相遇点长度为 b b b,环剩下长度为 c c c,那么总环长为 b + c b+c b+c,总链表长为 a + b + c a+b+c a+b+c。
因为fast
走过的路程为slow
的两倍,假设fast走了 n n n圈,那么有
a + n ( b + c ) = 2 ( a + b ) a+n(b+c)=2(a+b) a+n(b+c)=2(a+b)
得出
a = ( n − 1 ) ( b + c ) + c a=(n-1)(b+c)+c a=(n−1)(b+c)+c
那么此时再让一个指针ptr
从头开始,其走到环的起点的步数为 a a a,刚好就是slow
指针把【当前环剩下的步数走完】并且【再走 n − 1 n-1 n−1圈环】的步数。因此它们一起走,一定会在环的起点相遇,这就找到环的起点了。
完整代码
cpp
/**
* 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) {
ListNode *fast = head, *slow = head;
while (fast != nullptr) {
slow = slow->next;
if (fast->next == nullptr)
return nullptr;
fast = fast->next->next;
if (fast == slow) {
ListNode *ptr = head;
while (ptr != slow) {
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return nullptr;
}
};