📝 题目描述

题目链接环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例:

示例 1:

1
2
3
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

1
2
3
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

1
2
3
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 10^4]
  • -10^5 <= Node.val <= 10^5
  • pos 为 -1 或者链表中的一个有效索引

💡 解题思路

方法一:哈希表

创建一个集合,将遍历的节点放进去,如果出现了重复节点,则有环,否则没有。

方法二:快慢指针

快指针一次走两步,慢指针一次走一步。

如果有环,以慢指针作为参考系(假设慢指针静止不动),那么快指针相对于慢指针的速度就是:

vrelative=vfastvslow=21=1v_{relative}​=v_{fast}​−v_{slow}​=2−1=1

这意味着,在环里面,快指针正在以每次1步的距离一步一步地靠近慢指针。既然是每次精准地逼近 1 步,那么无论它们之间相隔多远的距离(假设为 N),距离的缩减轨迹只会是:NN1N2...210N → N-1 → N-2 ... → 2 → 1 → 0

距离变成 0 时,就是它们相遇的时刻。由于步长是 1,它不可能从距离为 1 直接变成距离为 -1(也就是跳过去)。

🔧 代码实现

1、哈希表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
unordered_set<ListNode*> node_set;
ListNode* ptr = head;
while (ptr != nullptr) {
if (node_set.count(ptr) != 0) {
return true;
}
node_set.insert(ptr);
ptr = ptr -> next;
}
return false;
}
};

2、快慢指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if (head == nullptr) return false;
ListNode* fast = head, *slow = head;
while ((fast -> next != nullptr) && (fast -> next -> next != nullptr)) {
fast = fast -> next -> next;
slow = slow -> next;
if (fast == slow) {
return true;
}
}
return false;
}
};

📊 复杂度分析

1、哈希表

  • 时间复杂度O(N)O(N),其中 N 是链表中的节点数。最坏情况下我们需要遍历每个节点一次。
  • 空间复杂度O(N)O(N),其中 N 是链表中的节点数。主要为哈希表的开销,最坏情况下我们需要将每个节点插入到哈希表中一次。

2、快慢指针

  • 时间复杂度O(N)O(N),其中 N 是链表中的节点数。当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮。
  • 空间复杂度O(1)O(1),除了两个指针外,不需要额外空间。

🎯 总结

  • 核心思想:掌握快慢指针的套路写法。