1. 原题如下
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
1.1 来源
leetcode 142题
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2. 思路与解法
如图所示,这是整个链表的模拟视图,其中A点是起点,B点即我们所要求的点,C点是某个点。
2.1 第一次快慢指针并行
我们采用快慢指针的思路,慢指针走一步,快指针走两步,
由于有环,他们会在某点相遇,设这个点为C;
慢指针走到C的路程为:AB+BC
快指针走到C的路程为:AB+BC+n*L
(其中n代表倍数,L代表整个圆的周长,当圆较小的时候,快指针可能会持续在圆内转圈,此时走很多圈)
并且,快指针比慢指针快一倍的速度所以
2*(AB+BC) = AB+BC+n*L
化简可以得出
AB = (n-1)*L+CB
2.2 第二次快慢指针并行
2.2.1 思路
此时我们将慢指针指向A点,快慢指针都以一倍速往前进行
他们再次相遇会正好为B。
2.2.2 原理
当慢指针走了AB的时候,快指针也走了长度与AB相等的路程,
从上面化简的条件我们可以知道,快指针走了长度为(n-1)*L+CB的路程
(n-1)*L代表转圈,相当于没动,从C点走了CB路程正好走到B点
3. 代码
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
if (head == head.next) {
return head;
}
ListNode slow = head.next;
ListNode fast = head.next.next;
while (slow != fast) {
if (slow == null || fast == null || fast.next == null) {
return null;
}
slow = slow.next;
fast = fast.next.next;
}
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}