# 环形链表
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/07/circularlinkedlist.png
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/07/circularlinkedlist_test2.png
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/07/circularlinkedlist_test3.png
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 前置条件
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 方法 1: 置空法
https://leetcode-cn.com/problems/linked-list-cycle/comments/113606
// 缺点:破坏了原链表(val&next关系)
// 使用特殊标记重写val,当通过next找到了该特殊标记,说明有环
var hasCycle = function(head) {
if (!head) {
return false;
}
let specialVal = Symbol("sp"); // 前端这里使用了 Symbol 的唯一性
while (head.next && head.val !== specialVal) {
head.val = specialVal;
head = head.next;
}
// 这里其实没用 当 specialVal = null 时有用
if (!head.next) {
return false;
}
return true;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 方法 2: 类似方法 1,也会破坏原链表
// https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode/161824
// 不断删除节点,如果有环,最终会出现一个节点的next指向自己
// 缺点破坏了原链表
var hasCycle = function(head) {
while (head != null) {
if (head == head.next) {
return true;
}
if (head.next != null) {
head.next = head.next.next;
}
head = head.next;
}
return false;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 方法 3: 哈希表(或者也可以在每个节点上增加新的标记,标识是否访问过)
// https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode/
// 这里使用 Map 数据类型,将 node 作为 key 保存
// 不能使用每个节点的 val 作为 key,因为不唯一
var hasCycle = function(head) {
let nodesSeen = new Map();
while (head != null) {
if (nodesSeen.has(head)) {
return true;
} else {
nodesSeen.set(head, "1");
}
head = head.next;
}
return false;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 方法 4: 双指针 优秀
// https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode/
var hasCycle = function(head) {
if (head == null || head.next == null) {
return false;
}
// 慢指针每次前进一步,快指针每次前进两步,快指针总是会追上慢指针
let slow = head;
let fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 方法 5: 数组判重,类似方法 3 只是 Map 换成了数组
// https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-by-alexer-660/
// 方法6: 标记法 类似方法1,只不过不再是修改 val,而是在 node 节点上增加新特殊属性来标记
// https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-by-alexer-660/
// 测试
function ListNode(val) {
this.val = val;
this.next = null;
}
var node1 = new ListNode(1);
var node2 = new ListNode(2);
var node3 = new ListNode(3);
node1.next = node2;
node2.next = node3;
node3.next = node1;
var head = node1;
console.log(hasCycle(head));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18