141. 环形链表
链接:https://leetcode-cn.com/problems/linked-list-cycle/
题目描述见链接内容。
解法1:标记链表
首先想到的是,在遍历这个链表时,为每个节点添加一个属性visited
,如果再次遍历到visited
为true
时,说明出现了环
var hasCycle = function (head) {
while (head) {
if (head.visited) {
return true;
}
head.visited = true;
head = head.next;
}
return false;
};
- 时间复杂度:
${O(N)}$
,N是链表中的节点数 - 空间复杂度:
${O(1)}$
,没有用到额外的空间 - 执行用时:85ms, 在所有JavaScript提交中击败了96%的用户,内存消耗:39.6MB,在所有JavaScript提交中击败了100%的用户
解法2:哈希表
原理与上一个解法相同,只是有了一个Set结构来存储已遍历过的节点
var hasCycle = function (head) {
const set = new Set();
while (head) {
if (set.has(head)) {
return true;
}
set.add(head);
head = head.next;
}
return false;
};
- 时间复杂度:
${O(N)}$
,N是链表中的节点数 - 空间复杂度:
${O(N)}$
,N是链表中的节点数 - 执行用时:96ms, 在所有JavaScript提交中击败了62%的用户,内存消耗:41.1MB,在所有JavaScript提交中击败了17%的用户
解法3:快慢指针
这种解法看了官方题解才了解到,挺巧妙的。声明了两个指针,fast
在slow
前面,fast
前进的速度也比slow
快,fast
一次前进两个,slow
前进一个
如果没有环的话,fast
肯定会率先完成所有遍历,如果有环的话,fast
会跑到slow
后面,总有一刻fast
会与slow
指向同一个节点
var hasCycle = function (head) {
if (!head) {
return false;
}
let fast = head.next;
let slow = head;
while (fast && fast.next) {
if (fast === slow) {
return true;
}
fast = fast.next.next;
slow = slow.next;
}
return false;
};
- 时间复杂度:
${O(N)}$
,N是链表中的节点数,没有 - 空间复杂度:
${O(1)}$
,只用了两个额外的指针 - 执行用时:88ms, 在所有JavaScript提交中击败了89%的用户,内存消耗:40.5MB,在所有JavaScript提交中击败了26%的用户