题目描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
解题思路一(Map)
如果一个链表不是环形链表,那么沿着链表进行遍历,最终会遍历到 null 节点的位置
如果一个链表存在环,那么在遍历到环的尾部节点(如下图的 -4 节点,只是做个比喻,环没有头尾之分)的时候,下一个位置会指向环的入口位置(如下图的 2 节点,也就是第二次遍历到 2 节点的时候)
因此,我们可以利用这个性质来进行解题
代码
/**
* 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 === null) return null
const mapper = new Map()
let p = head
while(p) {
if (!mapper.has(p)) {
mapper.set(p, p)
p = p.next
} else {
return p
}
}
return null
};
此处由于需要查找节点存放的位置,所以使用 map 结构会比使用数组结构的效率高
解题思路二(快慢指针法)
快慢指针法是同时设置两个指针,每次快指针比慢指针多走一步,如果两个指针能够再次相遇,说明这个链表有环,这个已经在[路飞]_环形链表说过了
从下图中我们可以看到 p、q自头节点之后第一次相遇的节点在 i 节点,而 i 节点到入环点的距离和头节点到入环点的距离是相等的
这不是巧合,而是可以验证的。
如图所示,假设 a -> c的距离是 x,c -> i的距离是 y,i -> c的距离是 z,由于相遇时 q 指针走的距离是 p 指针的两倍,会有这样的等式成立
2 * (x + y) = x + 2y + z,即 x = z
根据这个特性,我们可以编码解决这个问题
代码
/**
* 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 === null) return null
let p = q = head
do {
p = p.next
q = q.next.next
} while (p !== q && q && q.next)
// 如果不是因为相遇退出循环,则表示不存在环
if (p !== q) return null
// 两个指针分别从头节点和相遇节点开始遍历,相遇点即为入环点
p = head
while (p !== q) {
p = p.next
q = q.next
}
return p
};