题目:题目链接
题目大意:判断链表是否有环,如果有环就找出环的入口
思路:快慢指针。举个例子假设A和B都想从宿舍去操场跑步,A跑的速度是B的两倍,那么当A在操场跑步时,B可能还没到操场。但B到了操场A肯定还在操场。此时就变成了小学操场追人的问题,因为你们速度不一样,你们总会相遇的。
需要解决两个问题:1.如何判断有无环。2.如何找到环的入口。
开始解题:快慢指针可以判断 有无环,如果没环快指针肯定会指向null。那么问题变成了怎么找环的入口?
如下:
利用快慢指针,快指针比慢指针多走一步。
假设从起点到环的入口走了a步,然后走了b步到相遇点。那么此时慢指针走了a+b步,快指针走了2(a+b)步。而此时快指针也就走了a+b+kL(k为圈数L为环长)。注意:假设从相遇点到入口有c步,那么b+c=L。那么可以列式:
2
(
a
+
b
)
=
a
+
b
+
k
L
且
b
+
c
=
L
2(a+b) = a+b+kL 且 b+c=L
2(a+b)=a+b+kL且b+c=L
代人:
a
=
(
k
−
1
)
L
+
c
a=(k-1)L+c
a=(k−1)L+c
因为已知相遇点走了c步就到了环的入口,那么多走(k-1)L圈也能到入口,所以从相遇点走a (a=c+(k-1)L)步也能到环的入口。因为不知道a的长度,所以只要让一个指针从起点开始走,一指针从相遇点开始走,两者相遇就说明该点是环的入口。
这里的证明参考我之前的一篇回答:证明
所有,两个问题都解决了,那么看代码吧:
/**
* 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;//head,head->next有一个为空就表示不可能有环
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next) {//快慢指针走起来
slow = slow->next;
fast = (fast->next)->next;//走两步哦
if (fast == slow) {//代表有环
slow = head;//一个从起点走,一个从相遇点走
while (fast != slow) {//这里不是快慢指针哦,是两个指针一样快
slow = slow->next;
fast = fast->next;
}
return slow;//走到环的入口
}
}
return nullptr;//没有环
}
};
加油加油加油加油!!!!