给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例 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
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
进阶:
- 你是否可以不用额外空间解决此题?
一、思路
(一)暴力法
使用set去重,然后判读set的大小与链表长度是否一致,不一致则表示刚刚放入的集合的那个node是重复的
C++代码
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
set<ListNode *> detect;
int len = 0;
while (head != NULL) {
detect.insert(head);
len++;
if (len != detect.size())
break;
head = head->next;
}
return head;
}
};
执行效率:
(二)双指针法
设置两个指针,一个是快指针fast、一个是慢指针slow,将快指针的移动速度设为慢指针的两倍
设置循环,若在某次循环中fast追上了slow(实际上是超了一圈),表明这个链表存在回环
之后需要找到回环的第一个位置,怎么找呢?
假设入口为节点A,它离链表中的第一个节点的距离为a,快指针与慢指针相遇的节点为B,设节点B离回环的第一个节点的距离为b,则节点B离链表的第一个节点的距离为:(a+b)
我们知道此时快指针fast经过的节点数量是慢指针slow的两倍,即:2*(a+b)
于是用快指针走过的距离减去慢指针走过的距离,得到的就是快指针比慢指针多走的距离,这里,我们知道,快指针与慢指针相遇时,快指针一定比慢指针多走了一圈回环,因此可知回环的长度为:(a+b)
我们想要知道的是节点A,现在节点A离第一个节点的距离为a,而慢指针所在的节点B现在离节点的距离是b,根据回环长度,可知慢指针再经过a个节点后便能到达节点A,而第一个节点(head)经过a个节点后也恰好到达节点A,于是尽管我们并不知道a是多少,但是可以利用这个条件判断是否到达A点
C++代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
// 1 判断是否存在回环
ListNode *fast = head, *slow = head;
bool hasCycle = false;
while (fast != NULL) {
// fast移动两步,slow移动一步
fast = fast->next;
if (fast == NULL)
return NULL;
fast = fast->next;
slow = slow->next;
if (fast == slow) {
hasCycle = true;
break;
}
}
if (hasCycle) {
while (slow != head) {
slow = slow->next;
head = head->next;
}
return head;
}
return NULL;
}
};
执行效率: