题目
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
提示:
链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。
进阶:你能用 O(1)(即,常量)内存解决此问题吗?
解题思路
书接上回 环形链表1,这次标注了不能修改链表的要求,并且要求返回入环的第一个节点,所以不能直接修改链表的 next 指针了。先用 HashSet
的方法来解决问题,遍历链表,将访问过的节点存入 HashSet
中,如果发现当前节点已经在 HashSet
中,则说明有环,返回当前节点;如果遍历完链表都没有发现重复的节点,则返回 null。
1 | /** |
当然这个方法的空间复杂度是 O(n)
,不符合题目要求的 O(1)
空间复杂度。所以还是考虑快慢针,这里的问题在于当发现有环的时候并不是入环的第一个节点,所以需要思考如何找到入环的第一个节点。
说实话这如果不刷题还真的很难想到这里,仔细想的话,会发现快慢针有一个隐含条件,就是当fast
和slow
相遇的时候,fast
走过的距离是slow
走过的距离的两倍。而根据该条件就可以进行如下推导:
- 假设链表中环外的距离为
a
(也就是:链表头到入环点的距离为a
),入环点到相遇点的距离为b
,相遇点到入环点的距离为c
。 fast
指针走过的距离为,其中 n
是环的圈数。slow
指针走过的距离为,其中 m
是环的圈数。- 由于
fast
指针走过的距离是slow
指针走过的距离的两倍,所以可以得到如下方程: - 化简后可以得到:
即为
基于 b+c
就是环的周长,a
就是入环点到相遇点的距离,所以可以得到如下结论:无论fast
和 slow
指针相遇在环的哪个位置,在相遇的时候一个指针从链表头出发,另一个指针从相遇点出发,两个指针相遇的地方就是入环点。
所以可以得到如下代码:
1 | /** |