题目:
给定一个链表,如果它是有环链表,实现一个算法返回环路的
开头节点
。若环不存在,请返回null
。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况
~~~python # 样例1 输入:head = [3,2,0,-4], pos = 1 输出:tail connects to node index 1 解释:链表中有一个环,其尾部连接到第二个节点
样例2
输入:head = [1,2], pos = 0 输出:tail connects to node index 0 解释:链表中有一个环,其尾部连接到第一个节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<!--more-->
### 解题方法:
{% note warning%}
哈希表法思路比较简洁明了,需要注意的是存储的是node地址,而非值;快慢指针是很重要的技巧,需要熟练掌握,优点是时间复杂度和哈希表法相同,但是只用$O(1)$的额外空间
{% endnote %}
1. **哈希表法:**遍历链表,每经过一个node就把他的地址放到表里,第一个重复的地址即为圈的首节点
{% note primary %}
时间复杂度为$O(n)$,额外的空间复杂度为$O(n)$
{% endnote %}
3. **快慢指针:**
{% note primary %}
时间复杂度为$O(n)$,额外的空间复杂度为$O(1)$
{% endnote %}
### 解法一:哈希表法
{% note warning %}
- map、set、unordered_map、unordered_set总结:https://blog.csdn.net/qq_30815237/article/details/91047041
{% endnote %}
~~~c++
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while(head != NULL)
{
if(visited.count(head) == 1)
// if(visited.find(head) != visited.end())
// count实测比find函数快,执行时间分别是4ms和8ms
{
return head;
}
else
{
visited.insert(head);
head = head->next;
}
}
return NULL;
}
};
解法二:快慢指针
解题思路:
- 使用两个指针,
fast
与slow
。它们起始都位于链表的头部。随后,slow
指针每次向后移动一个位置,而fast
指针向后移动两个位置。如果链表中存在环,则fast
指针最终将再次与slow
指针在环中相遇- 此时
fast
走的距离为 \(a+n(b+c)+b\),此时slow
走了 \(a+m(b+c)+b\) 的距离 - 根据定义,
fast
走了slow
的两倍距离,因此 \(a+n(b+c)+b=2(a+m(b+c)+b)\) - 解得 \(a=(n-2m-1)*(b+c)+c\)
- 此时
- 相遇后:新建指针
p = head
从头走,slow
和p
一起走,当p
走了\(a\)步时,slow
也走了\((n-2m-1)*(b+c)+c\) 步,即\(n-1\)圈再加上额外的\(c\)步,即相遇时为环的起点
证明1:快慢指针一定会相遇吗?(数学归纳法)
- 如果两指针间距为一,则两者再走一次就相遇了
- 如果间距为n,则两者再走一次后,间距就变为 \(n+1-2=n-1\) 了,重复这个过程,直至两者相遇
证明2:快指针的速度是慢指针的n倍时,两者是否还会相遇?
1 | class Solution { |