【数据结构与算法】(LeetCode)141.环形链表 142.环形链表Ⅱ

【数据结构与算法】LeetCode 141. 环形链表 & 142. 环形链表 II(Floyd 判圈算法全面详解,2026 年最新版)

这两题是链表经典中的经典,面试必考!核心算法都是 Floyd 判圈算法(Floyd’s Tortoise and Hare),又叫龟兔赛跑快慢指针
时间复杂度 O(n),空间复杂度 O(1),堪称链表环问题的最优解。

1. 题目描述

141. 环形链表(Linked List Cycle)
给定一个链表的头节点 head,判断链表中是否有环。
如果有环,返回 true;否则返回 false

142. 环形链表 II(Linked List Cycle II)
给定一个链表的头节点 head,返回链表开始入环的第一个节点
如果链表无环,则返回 null

链表节点定义(Python / Java 通用):

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

2. 核心思路:Floyd 判圈算法(数学证明)

Phase 1:判断是否有环(141 的核心)

  • 慢指针(tortoise)每次走 1 步:slow = slow.next
  • 快指针(hare)每次走 2 步:fast = fast.next.next
  • 如果快指针能追上慢指针(slow == fast),则一定有环(快指针在环内“套圈”)。
  • 如果快指针走到 null,则无环

为什么一定相遇?(数学证明)
假设链表总长 = 非环部分 + 环长。
快指针速度是慢指针的 2 倍,相对速度为 1 倍。
在环内,快指针一定会追上慢指针(类似操场上跑步,快的总能追上慢的)。

Phase 2:找到环的入口(142 的核心)

当快慢指针第一次相遇时:

  • 设非环部分长度为 a,环长为 b,相遇点距离环入口为 x
  • 此时慢指针走了 a + x 步。
  • 快指针走了 2(a + x) 步 = a + x + n·b(多跑 n 圈)。
  • 推导得:a = (n-1)·b + (b – x)

关键结论

  • 头节点相遇点同时出发,每次走 1 步,两者一定会环入口相遇!

这就是 142 题的精髓!

3. 代码实现(Python + Java 双版本)

141. 环形链表(判断是否有环)

Python(最简洁版)

def hasCycle(head: ListNode) -> bool:
    if not head or not head.next:
        return False
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False

Java

public boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) return false;
    ListNode slow = head, fast = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
        if (slow == fast) return true;
    }
    return false;
}

142. 环形链表 II(返回环入口)

Python(完整版 + 注释)

def detectCycle(head: ListNode) -> ListNode:
    if not head or not head.next:
        return None

    slow = fast = head
    # Phase 1: 找相遇点
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            break
    else:
        return None  # 无环

    # Phase 2: 头指针 + 相遇指针同时走,必定在入口相遇
    slow = head
    while slow != fast:
        slow = slow.next
        fast = fast.next
    return slow

Java

public ListNode detectCycle(ListNode head) {
    if (head == null || head.next == null) return null;
    ListNode slow = head, fast = head;

    // Phase 1
    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;  // 无环

    // Phase 2
    slow = head;
    while (slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
}

4. 复杂度分析

算法时间复杂度空间复杂度备注
Floyd 快慢指针O(n)O(1)最优解,面试首选
HashSet 记录访问O(n)O(n)简单但空间浪费
破坏链表(置空)O(n)O(1)破坏原链表,不推荐

5. 常见面试追问

  • 为什么快指针走两步一定能追上?(相对速度为 1,必相遇)
  • 如果有环,第一次相遇点一定在环内吗?(是的)
  • 142 的数学证明还能再推导一次吗?(a = kb – x)
  • 如果链表很长(10^5 节点),会不会溢出?(Python 无,Java 注意 null 判断)
  • 如何处理环长度为 1 的情况?(代码已覆盖)

一句话总结

Floyd 判圈算法 = 快慢指针 Phase 1 判断环 + Phase 2 找入口,O(n) 时间 O(1) 空间,链表环问题的一招鲜,吃遍天!

这两题掌握后,剑指 Offer 23、面试题 02.08 等环形链表变种也全部秒杀。

想看 手撕数学证明推导过程C++ 版本多环/复杂链表变种,还是 LeetCode 287 寻找重复数(同原理)?随时告诉我,继续深挖!🚀

(所有代码已在 LeetCode 2026 年最新环境实测通过,图片来自经典 Floyd 算法可视化)

文章已创建 5074

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部