题目描述:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0
开始)。 如果 pos
是 -1
,则在该链表中没有环。注意,pos
仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:你是否可以使用 O(1) 空间解决此题?
示例 1:
1 | 输入:head = [3,2,0,-4], pos = 1 |
示例 2:
1 | 输入:head = [1,2], pos = 0 |
示例 3:
1 | 输入:head = [1], pos = -1 |
提示:
- 链表中节点的数目范围在范围 $[0, 10^4]$ 内
- $-10^5 <= Node.val <= 10^5$
pos
的值为-1
或者链表中的一个有效索引
链接:
https://leetcode-cn.com/problems/linked-list-cycle-ii
题目分析
这道题是 Leetcode 141.环形链表 的进阶,在判断链表是否有环的基础上还需要返回环的开始结点。我们在那道题中采用的是快慢指针的做法,这道题应该可以使用的方法,但是我们需要解决一个问题:怎么获得环开始的结点?我们知道,快指针的速度是慢指针的两倍,而它们是同时出发的,则快指针走的路程也是慢指针的两倍。我们分析它们走过的路程是什么样的。如下图所示。蓝色的点是它们的相遇点。
则我们可以知道,快指针走过的路程应该是 $a+n(b+c)+b$,而慢指针走过的路应该是 $a+b$。而快指针走过的路程是慢指针的两倍,则我们可以得到下面的等式
通过变换我们可以得到 $a=c+(n-1)(b+c)$,则我们可以发现,在快慢指针的相遇点出发,走 c
步就刚好到了环的入口,而链表头部到环入口的距离(也即 a
),就是从相遇点到环入口加上 n-1
个环(也即 b+c
)的距离。则我们可以在快慢指针相遇后,令一个新的指针从 head
出发,与慢指针同时前进,经过了 a
的单位时间后,他们的相遇点就是环的入口。
需要注意的是,当时在做前一道题的时候,我们使用的是 while
循环,让快慢指针先走了一步,它们的出发点不是 head
,因此他们的相遇点也不会是上面分析的那样。这道题我们使用了上一道题提到过的 do-while
循环,将出发点修改为 head
。
1 | class Solution { |
时间复杂度:$O(n)$,其中 $n$ 为链表的长度。我们知道快慢指针相遇所需要的时间是 $O(n)$,而相遇后慢指针和新指针走的路程为 a
,也不会超过 $n$。因此复杂度仍然是 $O(n)$。
空间复杂度:$O(1)$。我们只需要三个指针的空间。