- 思路
使用快慢指针的方法,让指针fast每次走两步,指针low每次走一步。如果链表中有环,那么fast和low必相交。为什么呢?为什么快指针不会每次正好跨过慢指针???我们可以使用相对“速度”来理解快慢指针。快指针相对于慢指针,每次只移动一格,两个指针都在环里时,此时每次移动,快指针和慢指针间隔减少1;所以快指针必然会追上慢指针并重合。
那么如何找到环的入口呢???
我们假设从链表头到环的入口处的距离是D,环的长度是S。这里假设S>D,且走的都是逆时针。当慢指针刚到入口处,此时快指针已经从入口处又走了D的距离,此时,快指针距离慢指针的距离是S-D,也就是说慢指针走S-D,就会和快指针相遇,假设相遇的位置到入口处的顺时针距离是N,也就是说S-D=N。也就是说,D=S-N。即相遇的位置距离环入口的逆时针距离是D,和表头到环入口位置的距离相同。(虽然没有图,把上面的文字转换成图,秒懂)
结论:利用这种办法判环,慢指针走不满一圈就可以确定有环;时间复杂度是O(N)(因为慢指针走的距离是D+N < S + D,判断出有环,找到入口节点,慢指针走的距离才是 D + S )
- 代码
/**
* 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;
ListNode *slow = head;
bool isHaveCycle = false;
while(fast != nullptr && fast -> next != nullptr){//判环
fast = fast -> next -> next;
slow = slow -> next;
if(fast == slow){
isHaveCycle = true;
break;
}
}
if(isHaveCycle){//找到环的入口
fast = head;
while(fast != slow){
fast = fast -> next;
slow = slow -> next;
}
return slow;
}
return nullptr;
}
};