题目链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/
思路
(1)设置快慢指针,快指针一次走2步,慢指针一次走1步;如果快指针遍历到空指针,则链表没有环;否则快指针将追上慢指针,两个指针在环内相遇
(2)快指针从头以相同步长同慢指针开始遍历,两个指针将在环的入口处相遇(有对应数学证明)
简单的证明如下:
设slow在第一阶段走的路程为k
,则fast走的路程为2k
,同时设从起点到入口节点的长度为l
,环的长度为c
。
则slow走到环内的位置 x = k - l
,slow相对入口节点的距离为x
。又因为fast和slow在圈内相遇,fast在圈内的位置=slow在圈内位置(fast多走了一圈环),则有 2k-l = x + k
。
证明第二阶段fast和slow会在入口处相遇
第二阶段,slow最终在环内的位置为 x+l = x + (k-x) = k
,k%k = 0
,即此时到达的是环的入口节点!!这就证明了第二阶段fast和slow相遇的点为环的入口节点
复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)
代码
/**
* 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) {
if(!head || !(head->next) ) return nullptr;
ListNode *fast = head; // 快指针一次走2步
ListNode *slow = head; // 慢指针一次走1步
do{
fast = fast->next;
if(fast) fast = fast->next;
slow = slow->next;
} while (fast!=nullptr && fast!=slow);
// fast和slow在环内相遇; 则表示有环存在
if(fast == nullptr && slow!=nullptr) return nullptr; // fast为空没有环
// fast从头以相同步长遍历,相遇节点为第一个节点
for(fast = head; fast!=slow;){
fast = fast->next;
slow = slow->next;
}
return fast;
}
};