判断链表的入环节点
首先我们将令快慢指针都从Head出发,Head到H的距离设为A,H到Y节点的距离设为B,而环一圈的距离设为C。
设快指针的速度为慢指针的两倍,快指针走了m圈和慢指针走了n圈时相遇,可以列出式子1:
fast指针的路程 = A + B + m * c, slow指针的路程 = A + B + n * c
相同时间下,fast指针的路程是slow指针的两倍,可列出式子2:
又因为当fast指针与slow指针首次相遇时,在环中的走过的圈数比slow指针的走过的圈数多一,可列出式子3:
m = n + 1
式子2带入式子1,得到式子4:
A + B + m * c = 2(A + B + n * c)
再将式子3带入式子4,可得到:
(n+1) * c = A + B + 2n * c (1 - n) = A + B
当链表Head 不为null,时:
A + B > 0 ,且圈数n > 0
我们可以得到n的范围是:
0 < n <= 1
当0 < n < 1时,该链表有环,且入环节点不为Head节点。
当n = 1时,链表的时,fast指针和slow指针在Head节点相遇,也就是fast指针超过slow指针一圈。
总上所述,
slow指针与fast指针第一次相遇时,slow指针并未走完环的一圈。
重新分析链表,分别设Head到入环节点的距离为X,入环节点到相遇节点的距离为Y,相遇节点到入环节点的距离为Z。(因为该链表为单链表)
通过式子2,可列出:
X + 2 * Y + Z = 2 (X + Y)
可得出:
Z = X
所以,我们可以让快慢指针第一次相遇时,记录下相遇的节点位置,再让其中一个指针节点重新指向Head,让他们继续以相同速度前进,当他们再次相遇时,相遇节点就为入环节点。
public ListNode EntryNodeOfLoop(ListNode Head) {
//先判断Head是否为null,当Head节点为null时返回null;
if (Head == null) return null;
// 定义快慢指针
ListNode slow = Head;
ListNode fast = Head;
while (fast != null && fast.next != null) {
// 将快指针速度设为慢指针的两倍;
fast = fast.next.next;
slow = slow.next;
// 当快慢指针相遇时,跳出循环;
if (slow == fast) break;
}
//当遍历完链表没找到slow == fast时,说明该单链表不成环;
if (fast == null || fast.next == null) return null;
//因为我们知道Head到入环节点的距离等于相遇节点到入环节点的距离;
//所以我们可以让其中一个指针重新指向Head;
fast = Head;
//再次让快慢指针以相同的速度出发,当他们再次相遇时,相遇节点就为入环节点
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}