142. 环形链表 II
题目链接:
题目描述:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 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
解释:链表中没有环。
算法:
此题其实分为两个部分,检测环形链表和找到循环的起始位置。
检测环形链表其实很简单,141. 环形链表就是一道这样的题目,题目也有官方题解,官方题解十分详细。解法有两个:
-
建立一个哈希表,每经历一个元素就检测哈希表中十分存在这个元素,是的话就返回true,如果当前结点位null,则返回false。时间复杂度为:,空间复杂度:。
-
使用快慢两个指针。如果不存在环,那么快指针会率先达到终点;如果存在环,则好像环形赛道上跑步的运动员,快的肯定会追上慢的。并且由于慢的跑半圈,快的就可以跑一圈,所以慢指针进入循环之后,只需要跑半圈循环肯定会与快指针相遇。时间复杂度为:,空间复杂度:。
对于这道题,如果同样用哈希表,那么就与检测环形链表的代码一样了,也就没有挑战性了。这道题最难的部分是如何使空间复杂度为。
我们同样用快慢指针检测环形链表。假设进入循环第一个结点前的长度为,整个循环的长度为,快指针与慢指针相遇的结点距离循环的第一个结点的距离为。那么根据之前的分析可知,慢指针走过的距离为,快指针走过的距离是。并且。由此可知,。也就是说,整个循环的长度刚刚好是慢指针走过的路程。
现在我们要求的是a,我们就可以利用这个等式。因为慢指针已经走过的距离了,如果它再走个结点,则,这刚刚好代表了循环的第一个结点的位置。而如果我们再用一个新的指针,让它从开始走步,它同样在循环的第一个结点,与原结点相遇。这样,我们就找到了题目要求的循环的第一个结点。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *p = head, *f = head;
while (f != NULL && f->next != NULL) {
p = p->next;
f = f->next->next;
if (p == f) {
f = head;
while (p != f) {
p = p->next;
f = f->next;
}
return f;
}
}
return NULL;
}
};