这题的题解中快慢指针法的理解涉及到公式推导,将自己的理解先记下来,防止以后忘记。
题目
题解1
题解1就是使用 set 作为哈希表,没啥难度,不过需要额外的空间。
思路:
利用 set 容器的唯一性存下每个遍历过的结点,当再次遇到同一结点时该结点即为环的入口,直接返回;若一直到遍历结束也没有相同结点,表示链表无环,返回NULL。
分析: 时间复杂度 O(n),空间复杂度 O(n)
实现:
ListNode *detectCycle(ListNode *head) {
set<ListNode*> visited; // 已访问过的结点
while(head){
if(visited.find(head) != visited.end()){ // 发现重复结点
return head;
}else{
visited.insert(head);
}
head = head->next;
}
return head;
}
提交结果:
提交了好几次,最快就是28ms了,时间应该是消耗在 set 的 find 和 insert 上了。
题解2
思路:
先使用快慢指针,判断链表是否有环;然后重新设置两个指针分别从链表头结点和快慢指针相遇的结点开始,继续遍历结点,当这俩指针相遇时,相遇的结点即为环的入口结点。
详细分析:
若环不存在直接返回 NULL。
若存在环,设 非环结点为 a 个,环内的结点为 b 个,
同时设 快慢指针第一次相遇时快指针已在环内走了
k
∗
b
+
c
k*b + c
k∗b+c 步
(
k
=
1
,
2...
,
0
≤
c
<
b
)
( k = 1,2... ,0 \leq c < b)
(k=1,2...,0≤c<b),(注意,k 最小为 1) 且相遇点距入环处有
b
−
c
b - c
b−c 个结点的距离。如下图:
所以,由公式:
2
∗
慢
指
针
的
路
程
=
快
指
针
的
路
程
2 * 慢指针的路程 = 快指针的路程
2∗慢指针的路程=快指针的路程, 可得:
2
∗
(
a
+
c
)
=
a
+
k
∗
b
+
c
2 * (a + c) = a + k*b + c
2∗(a+c)=a+k∗b+c,
化简得
a
=
(
k
−
1
)
∗
b
+
(
b
−
c
)
a = (k - 1)*b + (b - c)
a=(k−1)∗b+(b−c)。
因此,接下来我们只需要设两个速度相同的指针同时从快慢指针第一次相遇的结点以及链表头结点出发,他们最终会相遇于环入口结点。
时间复杂度 O(n),空间复杂度 O(1) 。
实现:
ListNode *detectCycle(ListNode *head) {
if(head == NULL){
return NULL;
}
ListNode* p1 = hasCycle(head); // 判断是否有环并指向第一次相遇的结点
if(p1 == NULL){
return NULL;
}
ListNode* p2 = head; // 从头开始的指针
while(p1 != p2){
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
// 使用快慢指针遍历链表,判断是否有环,有则返回快慢指针的相遇结点
ListNode* hasCycle(ListNode *head){
ListNode *slow = head;
ListNode *fast = head;
while(fast != NULL && fast->next != NULL){ // 若不存在环,快指针会最先到达链表尾部
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
return slow;
}
}
return NULL;
}
提交结果: