1、解题思路
快慢指针法(Floyd's Tortoise and Hare Algorithm):
- 判断是否有环:使用快慢指针,快指针每次移动两步,慢指针每次移动一步。如果两者相遇,说明有环;否则无环。
- 找到环的入口:当快慢指针相遇时,将其中一个指针(如慢指针)重新指向链表头部。然后两个指针每次同时移动一步,再次相遇的节点即为环的入口。
数学证明:
- 设链表头到环入口的距离为 a,环入口到相遇点的距离为 b,相遇点到环入口的距离为 c。
- 快指针走的距离:a + b + c + b(因为快指针比慢指针多走一圈)。
- 慢指针走的距离:a + b。
- 因为快指针速度是慢指针的两倍,所以 2(a + b) = a + b + c + b => a = c。
- 因此,重新将一个指针指向头节点,两指针再次相遇时即为环的入口。
2、代码实现
C++
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
ListNode* slow = pHead, *fast = pHead;
// 判断是否有环
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
break; // 相遇, 有环
}
}
// 无环的情况
if (!fast || !fast->next) {
return nullptr;
}
// 重新将一个指针指向头节点
slow = pHead;
// 两个指针同时移动, 直到再次相遇
while ( slow != fast) {
slow = slow->next;
fast = fast->next;
}
// 相遇点即为环的入口
return slow;
}
};
Java
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode slow = pHead, fast = pHead;
// 判断是否有环
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) break; // 相遇,有环
}
// 无环的情况
if (fast == null || fast.next == null) return null;
// 重新将一个指针指向头节点
slow = pHead;
// 两个指针同时移动,直到再次相遇
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
// 相遇点即为环的入口
return slow;
}
}
Python
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
slow, fast = pHead, pHead
# 判断是否有环
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break # 相遇,有环
# 无环的情况
if not fast or not fast.next:
return None
# 重新将一个指针指向头节点
slow = pHead
# 两个指针同时移动,直到再次相遇
while slow != fast:
slow = slow.next
fast = fast.next
# 相遇点即为环的入口
return slow
3、复杂度分析
- 时间复杂度:O(n),最多遍历链表两次。
- 空间复杂度:O(1),只使用了两个指针。

京公网安备 11010502036488号