环形链表Ⅱ

题目

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
不允许修改链表

  • 示例1:

    1
    2
    3
    输入:head = [3,2,0,-4], pos = 1
    输出:返回索引为 1 的链表节点
    解释:链表中有一个环,其尾部连接到第二个节点。
  • 示例2:

    1
    2
    3
    输入:head = [1,2], pos = 0
    输出:返回索引为 0 的链表节点
    解释:链表中有一个环,其尾部连接到第一个节点。
  • 示例3:

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

进阶:

  • 使用 O(1) 空间解决此题

思路+代码

本题可以采用哈希表与快慢指针的方式分别完成解题

哈希表实现算法

  • 通过遍历链表中的每个节点,并将节点存储在哈希表中
  • 当出现加入的节点已经存在哈希表中,就可以判定链表中存在环,否则表明链表没有环

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
if (visited.count(head)) {
return head;
}
visited.insert(head);
head = head->next;
}
return nullptr;
}
};

快慢指针实现算法

通过设定两个指针 fastslow,均位于链表头部。slow 指针每次移动一位,fast 指针每次移动两位,如果链表中存在环,则 fast 指针最终会追赶上 slow 指针并在环中相遇

假设链表有环,会有两种环情况:

  • 环很大

假如他们在相遇点相遇了,那么慢指针走过的距离是 a+b,快指针走过的距离就是 a+b+c+b。因为相同时间内快指针走的距离是慢指针的 2 倍,所以有 a+b+c+b = 2*(a+b),即 a=c,也就是说从相遇点到环的入口和链表的起始点到环的入口,距离是一样的。在相遇点的时候我们可以使用两个指针,一个从相遇点开始,一个从链表头开始,他们每次都走一步,直到他们再次相遇位置,那么这个相遇点就是环的入口。

  • 环很小

那么这种情况,快指针在环上转了好几圈了,慢指针才走到环上,假如快指针在环上已经走了 m 圈了,慢指针在环上走了 n 圈,他们最终在环上相遇,那么

  • 慢指针走过的距离是:a+b+n*(b+c) (b+c其实就是环的长度)
  • 快指针走过的距离是:a+b+m*(b+c)
    在相同的时间内快指针走过的距离是慢指针的2倍,所以有
  • a+b+m*(b+c) = 2*(a+b+n*(b+c))
    整理得到
  • a+b=(m-2n)(b+c)
    上面 b+c 其实是环的长度,也就是说 a+b等于 (m-2n) 个环的长度。这个时候我们还可以使用两个指针一个从相遇点开始,一个从链表头开始,这时候就会出现一个现象就是一个指针在链表上走,一个指针在环上转圈,最终会走到第 1 种情况,就是环很小(我们可以认为链表前面减去 m-2n-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
25
26
27
28
29
30
31
32
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head == NULL || head->next == NULL)
return NULL;
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && slow != NULL)
{
slow = slow->next;
fast = fast->next;
if (fast != NULL)
fast = fast->next;
else
return NULL;

if (slow == fast)
break;
}

if (fast == NULL || slow == NULL)
return NULL;

slow = head; // slow指针在相遇点之前的a路上走,fast指针有可能在环上走多次
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return fast;
}
};

作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/linked-list/jjhf6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。