算法 判断链表是否存在环并找出环入口
@author:Jingdai
@date:2020.12.26
题目描述
判断给定的链表中是否存在环,如果存在环的话找出环的入口并输出,如果不存在环的话就输出
null
。
思路及代码
首先第一个问题是如何判断一个链表中是否存在环呢?熟悉快慢指针的童靴一定知道判断链表是否存在环是一个典型的可以用快慢指针解决的问题。
我们用两个指针,一个慢指针 slowPointer
,一个快指针 fastPointer
。他们都从头开始走, 慢指针一次走一步,快指针一次走两步,如果存在环的话快慢指针一定会相遇,根据此来判断是否存在环。
下面一个问题就是如何找到环的入口了。这个问题稍微复杂一点,看下图。
环外长度为 l
,红色的点为相遇点。相遇时慢指针走了 l + a
,快指针走了 l + n (a + b) + a
,n
为圈数,快指针走的距离是慢指针的2倍,化简可以得到 l = (n - 1) (a + b) + b
。设环周长为 c
,可以得到 l = (n - 1) c + b
,即 l
的距离和在环内转 n-1
圈再走 b
的距离相同。此时如果从相遇点走这么长距离,正好会到达环入口。我们可以再设置一个指针从头开始,另一个指针从相遇点开始走,当他们相遇时,即都走了 l
时,他们就会在环入口点相遇,根据此就可以找到环入口了。
注意:细心的人可能会想为什么慢指针在环内只走了 a
,而不会走了一圈加 a
呢?因为慢指针只会在一圈内和快指针相遇。慢指针进入环时,快慢指针的距离肯定是小于环的周长的,这里我们取个极限,假设快慢指针的距离等于环周长(实际取不到)。那么快慢指针都在环入口处,当慢指针走一圈,快指针走两圈时,他们会再次在环入口处相遇,即距离最大值时慢指针也只会走一圈,同时这个距离最大值是取不到的,那么慢指针也不会走到一圈,所以慢指针一定会在一圈内和快指针相遇。
最后的代码如下。
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode dummy = new ListNode();
dummy.next = head;
ListNode fastPointer = head.next;
ListNode slowPointer = head;
while (fastPointer != null && fastPointer.next != null) {
slowPointer = slowPointer.next;
fastPointer = fastPointer.next.next;
if (fastPointer == slowPointer) {
// hasCicle
slowPointer = dummy;
while (slowPointer != fastPointer) {
slowPointer = slowPointer.next;
fastPointer = fastPointer.next;
}
return slowPointer;
}
}
return null;
}