- 设置快慢指针,寻找数量关系。快指针为fast,慢指针为slow。快指针一次走2步,满指针一次走一步。
- 则如果链表中有环,快慢指针最终一定会在环上相遇。
- 相遇时,快指针比满指针多走整数圈。
设相遇时,快指针走的步数为fast.lengthfast.lengthfast.length,满指针走的步数为slow.lengthslow.lengthslow.length,图中链表圈的长度为cycle.lengthcycle.lengthcycle.length。有fast.length=2∗slow.lengthfast.length = 2 * slow.lengthfast.length=2∗slow.length
- 则第一次相遇时具有数量关系。
fast.length−slow.length=n⋅cycle.lengthfast.length - slow.length = n\cdot cycle.lengthfast.length−slow.length=n⋅cycle.length - 慢指针第一次与快指针相遇时,慢指针至多走完一圈;
- 考虑当慢指针刚进入链表的时刻,快指针就在慢指针之前 +1的距离的情形。这时快指针要追上慢指针需要的时刻最长。
- 慢指针走完一圈时,快指针已经走完两圈。所以慢指针在走完一圈中一定存在某一位置与快指针相遇。
- 于是slow.length=n⋅cycle.lengthslow.length = n\cdot cycle.lengthslow.length=n⋅cycle.length
设慢指针进入圈之前走过的步数为xxx,进入圈之后走过的步数为yyy。则slow.length=x+y=n⋅cycle.lengthslow.length = x + y = n \cdot cycle.lengthslow.length=x+y=n⋅cycle.length
- 所以从链表头到环起始结点的步数x=n⋅cycle.length−y(a)x = n \cdot cycle.length - y(a)x=n⋅cycle.length−y(a)
考虑快慢指针相遇的位置,距离链表环起始结点的步数为cycle.length−y(b)cycle.length - y\quad(b)cycle.length−y(b)
由式a和式b的数量关系可知,可以从链表头结点和相遇结点,分别设置每次都走一步的指针(设为指针AAA和BBB)。
- 当两个指针第一次相遇时,相遇结点一定存在于环上。
- 且相遇结点一定是环起始结点,这是因为:
- 首先相遇时两个指针走的步数相同。
- 当指针AAA第一次进入环中第一个结点,需要走的步数为n⋅cycle.length−yn \cdot cycle.length - yn⋅cycle.length−y,而此时指针BBB走过的距离也为这个值。当B走过这个值时,由式B可知B恰好处于进入环的首个结点。因此这个进入环的首个结点即目标结点恰好是他们俩第一次相遇的位置
AC代码如下
public ListNode detectCycle(ListNode head) {
if(head == null)
return null;
ListNode slow = head;
ListNode fast = head;
while(slow!=null && fast!=null)
{
slow = slow.next;
fast = fast.next;
if(fast == null)
break;
fast = fast.next;
if(slow!= null && slow == fast) //相遇代表找到了图中的圈
{
fast = head;
while(slow!=fast)
{
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}