对于一个给定的链表,返回环的入口节点,如果没有环,返回null
拓展:
你能给出不利用额外空间的解法么?
解答:
类似于追及问题:
-
如何判断有环的存在?
在追及问题中,我们可以用两个速度不同的物体从同一地点出发,如果相遇则证明存在环(可用反证法证明,若不存在环,则速度不同的物体从同一地点出发则一定不会相遇),因此可以类比过来,定义两个指针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的位置。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
slow = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
break;
}
}
if(fast == null || fast.next == null ) return null;
return slow;
}
}