题目链接
题目分析
类似于追及问题:
-
如何判断有环的存在?
在追及问题中,我们可以用两个速度不同的物体从同一地点出发,如果相遇则证明存在环(可用反证法证明,若不存在环,则速度不同的物体从同一地点出发则一定不会相遇),因此可以类比过来,定义两个指针fast、slow,令两指针以不同速度向后指,则相遇时证明有环存在,若fast指向NULL,则不存在环。 -
怎么找到环的入口结点?
首先说方法:在问题一中两指针相遇后,让一个指针从头结点开始,另一个从相遇结点开始,并以相同速度向后指,再次相遇时就是环的入口结点。
证明:
1)假设存在环,fast以速度2运行,slow以速度1运行,在slow走到入口t时,如图(m1为在slow首次到t时fast的位置,a为h到t的距离,b为t到m1的距离,n为环的周长):
由图知fast走的距离为a+b+xn
,slow走的距离为a
,又v(fast) = 2*v(slow)
,所以x(fast) = 2*x(slow)
,即2a = a+b+xn
,因此a = b+xn
。
m1逆时针到t的距离为n-b。
2)在首次相遇时,如图(m2为相遇点):
由于m1逆时针到t的距离为n-b,即要达到相遇需要追赶n-b
的距离,由于两者速度差为1,因此需要n-b
的时间才能相遇,此时slow再次向后n-b距离,即到达m2位置与fast相遇,因为一周长度为n,因此到t的距离为 n-(n-b) = b
。
3)为何令slow重新从pHead以速度1开始走,令fast从m2以速度1走?要想在入口t相遇,则需要从m2处再走b+yn
的距离,刚好pHead处符合(由1)可知,同时因为是一个环,所以即使相差整数圈的距离也是在圈的同一个位置),所以令slow从pHead开始走。在相遇后就是入口t的位置。
代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if ( !pHead ) {
return NULL;
}
ListNode *fast = pHead, *slow = pHead;
while( fast && fast->next ) {
slow = slow->next;
fast = fast->next->next;
if( slow == fast ) {
slow = pHead;
while( slow - fast ) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return NULL;
}
};