携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
LeetCode 142:环形链表II
题目描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改链表。
解题思路
思路一:利用Map、Set、WeakMap、WeakSet
在之前的一篇LeedCode 141:环形链表 - 掘金 (juejin.cn)中我们解决了环形链表问题,其中有一种解法是利用了ES6中WeakMap或WeakSet数据结构。
同样,这一道题目我们一样可以使用,这次我们使用Set,通过遍历及使用has(value)方法判断value值是否为Set的成员,如果是的话就说明有环,直接返回,否则使用add()方法向 Set 结构加入成员,因为 Set 结构不会添加重复的值
代码如下:
var detectCycle = function (head) {
if (head === null) {
return null;
}
const visited = new Set();
while (head !== null) {
if (visited.has(head)) {
return head;
}
visited.add(head);
head = head.next;
}
return null;
};
时间复杂度: O(n); 最坏情况下我们需要遍历每个节点一次
空间复杂度: O(n);
思路二:利用快慢指针
思路如下:
-
先判断有无环(设置快慢两个指针,遍历链表,快指针一次走两步,慢指针一次走一步,如果存在环,则快慢指针一定会指向同一节点,否则当快指针或快指针的下一个节点指向null时,则表明链表无环);
-
当快慢指针指向同一节点时,将快指针移动到head,快慢指针都一次走一步,相遇就返回快指针或慢指针;
其中第一步我们在 LeedCode 141:环形链表中已经实现过,很容易理解;
第二步理解:
如图, 设链表中环外部分长度为a。 slow指针进入环后,又走了b的距离与fast指针相遇。 此时,fast指针已经走完了环的n(n>=1)圈, 因此它走过的总距离为a+n(b+c)+b = a + (n+1)b + nc。
在任意时刻,fast指针走过的距离都为slow指针的2倍。因此:
a + (n+1)b + nc = 2(a + b) => a = c + (n -1)(b + c);
此时我们会发现: 如果从相遇点到入环点的距离加上n-1圈的环长,恰好等于从链表头部到入环点的距离。
实现代码如下:
var detectCycle = function (head) {
if (head === null) {
return null;
}
let slow = head, fast = head, isCycle = false;
while(fast.next !== null && fast.next.next !== null) {
fast = fast.next.next;
slow = slow.next;
if (fast === slow) {
isCycle = true;
break;
}
}
if (!isCycle) {
return null;
}
fast = head;
while (fast !== slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
时间复杂度: O(n);
空间复杂度: O(1);