题目描述
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入: head = [3,2,0,-4], pos = 1
输出: 返回索引为 1 的链表节点
解释: 链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入: head = [1,2], pos = 0
输出: 返回索引为 0 的链表节点
解释: 链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入: head = [1], pos = -1
输出: 返回 null
解释: 链表中没有环。
提示:
- 链表中节点的数目范围在范围
[0, 104]
内 -105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引
分析解答
以下分析和图解部分来自代码随想录
这道题有两个关键点:
- 是否有环
- 有环需要找到环的入口
是否有环
有环需要使用双指针(快慢指针)来判断。比如使快指针一次移动两个节点,慢指针一次移动一个节点,当进入环中,慢指针一定会在某个时刻追上快指针。举个例子:
- 1,2,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5 – 慢
- 3,5,1,3,5,1,3,5,1,3,5,1,3,5,1,3,5,1,3,5,1,3,5 – 快
找出环的出口
第一次接触真的会一脸懵 O.o
以下两图很好地解释了为什么 slow 只在转第一圈的时候一定会被 fast 追上。
所以可以列出等式((n >= 1)):
- slow = x + y
- fast = x + y + n(y + z)
- 2(x + y) = x + y + n(y + z)
- x + y = n(y + z)
- x = n(y + z) - y
- x = (n - 1)(y + z) + z
所以当 n 为 1 的时候, x = z。代表着如果有一个节点在相遇处,一个节点在head处,他们同时出发,刚好会在环形入口节点处相遇。
n 表示相遇的时候,快指针已经在环内转的圈数。
完整代码:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function(head) {
if(!head || !head.next) return null;
let fast = head.next.next
let slow = head.next
while (fast && fast.next) {
slow = slow.next
fast = fast.next.next
if (fast == slow) {
slow = head
while (fast != slow) {
fast = fast.next
slow = slow.next
}
return slow
}
}
return null
};