Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Note: Do not modify the linked list.
Follow up:
Can you solve it without using extra space?
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
说明:不允许修改给定的链表。
进阶:
你是否可以不用额外空间解决此题?
/**
* 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) {
if (head == null || head.next == null) return null;
ListNode runner = head;
ListNode walker = head;
while (runner.next != null && runner.next.next != null) {
walker = walker.next;
runner = runner.next.next;
if (walker == runner) {
ListNode walker2 = head;
while (walker != walker2) {
walker = walker.next;
walker2 = walker2.next;
}
return walker; // 考虑只有一个结点自环,不能在第二个while里面判断
}
}
return null;
}
}
原因分析:
问题:为什么快慢指针在相遇时再设置一个指针walker2从头开始慢走,然后第二个指针walker在环里慢走,能够再次相遇,并且再次相遇结点就是入口结点呢?
下面我们来证明这一点:
当它遇到慢速指针walker时,快速指针runner可能会运行几个圆而不是一个圆。
假设快速指针runner运行m个圆圈,并在它们相遇时慢速指针walker运行n个圆圈。然后我们可以得到如下结论:
runner走的距离 = walker走的距离 * 2 (因为walker每次一步,runner每次2步,距离肯定是2倍)
a + m *(b + c)+ b = 2 * (a + n *(b + c)+ b)
因此我们化简可以得到: mb + mc = a + (2n + 1)b +2nc
有两种可能性:
待定系数法,以b为准
m = 2n+1 -------①
m = a/c+2n -------②
由①②得出
a=c
推出a=c成立,所以在快慢指针相遇点再设置一个指针walker2从头开始慢走,第二个指针walker在环里慢走,一定能够相遇,并且再次相遇就是入口点。
待定系数法,以c为准
m = 2n -------------①
m = a/b + 2n + 1 ------------②
由①②得出
a = -b
而距离不可能是负数,所以这种情况不成立!也不是我们所关心的结论!
综上所述,只有a=c成立!
那么,我们只需要在快慢指针相遇点再次设置一个指针从头开始走,在环里的慢指针只走一轮就一定可以和从头到环的入口点的指针相遇,并且相遇点是环形链表的入口结点。于是就有了如下部分的代码:
if (walker == runner) {
ListNode walker2 = head;
while (walker != walker2) {
walker = walker.next;
walker2 = walker2.next;
}
return walker; // 考虑只有一个结点自环,不能在第二个while里面判断
}