题目描述:
题号:142
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。
不允许修改 链表。
解题思路:
思路一:快慢指针
快速记忆法:先两指针相遇,再制造一次相遇(相遇条件是快指针重新指向 head,然后快慢指针一起移动的相遇点是答案)
原理:Floyd 的环形链表检测算法。
快指针(fast)每次移动两个节点,而慢指针(slow)每次移动一个节点。
1、如果链表中存在环,那么快指针最终会追上慢指针,即它们会在某个节点相遇。
2、如果链表中不存在环,那么快指针会先到达链表的末尾(即 fast 或 fast -> next 会变为 nullptr),此时算法会返回 nullptr,表示没有检测到环。
3、在检测到环后,算法进入第二个阶段,fast 指针被重置为头节点 head,然后两个指针都以相同的速度移动,直到它们再次相遇。相遇点就是环的入口节点。
这是因为从头节点和相遇点同时出发的两个指针,在环内的移动路径长度是相同的,所以它们会在环的入口节点相遇。
时间复杂度:O(N)
空间复杂度:O(1)
C++
// C++
/**
* 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) {
if(head == nullptr || head->next == nullptr) {
return nullptr;
}
ListNode *fast = head, *slow = head;
while(true) {
if(fast == nullptr || fast->next == nullptr) {
return nullptr;
}
fast = fast->next->next;
slow = slow->next;
if(fast == slow) {
break;
}
}
// 第二轮相遇
fast = head;
while(slow != fast) {
slow = slow->next;
fast = fast->next;
}
return fast;
}
};
go
// go
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return nil
}
fast, slow := head, head
for true {
if fast == nil || fast.Next == nil {
return nil
}
fast = fast.Next.Next
slow = slow.Next
if fast == slow {
break
}
}
fast = head
for slow != fast {
slow = slow.Next
fast = fast.Next
}
return fast
}