【LeetCode 热题 100】141. 环形链表——快慢指针

Problem: 141. 环形链表

题目:给你一个链表的头节点 head ,判断链表中是否有环。

文章目录

整体思路

这段代码旨在解决一个经典的链表问题:环形链表 (Linked List Cycle) 。问题要求判断一个给定的单链表是否存在环,即链表中某个节点的 next 指针是否指向了它前面已经出现过的节点。

该算法采用了大名鼎鼎的 Floyd判圈算法(Floyd's Cycle-Finding Algorithm) ,也常被称为 "快慢指针""龟兔赛跑" 算法。这是一种极其高效且空间优化的方法。

  1. 核心思想:速度差追及

    • 算法的逻辑非常直观,就像在一个环形跑道上进行龟兔赛跑。
    • 设置两个指针,一个"慢"指针 slow 和一个"快"指针 fast,都从链表的头节点 head 出发。
    • slow 指针每次移动一步。
    • fast 指针每次移动两步。
    • 如果链表中存在环fast 指针会先进入环,并在环内不断地绕圈。slow 指针稍后也会进入环。一旦两个指针都进入了环,fast 指针由于速度更快,它会从后面"追上"slow 指针。它们最终必然会在环内的某个节点相遇。
    • 如果链表中不存在环fast 指针由于走得更快,它会首先到达链表的末尾(即 fastfast.next 变为 null)。一旦它到达末尾,就说明整个链表已经被遍历完且没有环。
  2. 算法实现步骤

    • 初始化 slowfast 指针都指向 head
    • 进入一个 while 循环。循环的条件 fast != null && fast.next != null 是至关重要的:
      • fast != null:确保 fast 指针本身是有效的。
      • fast.next != null:确保 fast 还能再走一步(因为 fast 每次要走两步 fast.next.next)。如果这个条件不满足,说明链表已经到头了。
    • 在循环内部:
      a. 移动指针slow = slow.next;fast = fast.next.next;
      b. 判断相遇 :在移动后,立即检查 slowfast 指针是否指向了同一个节点 (if (slow == fast))。
      • 如果它们相等,说明快指针追上了慢指针,证明链表中存在环。立即返回 true
    • 如果 while 循环正常结束(即因为 fast 到达链表末尾而终止),则说明在整个遍历过程中快慢指针没有相遇。因此,链表中不存在环,返回 false

完整代码

java 复制代码
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    /**
     * 判断一个单链表是否存在环。
     * @param head 链表的头节点
     * @return 如果存在环则返回 true,否则返回 false
     */
    public boolean hasCycle(ListNode head) {
        // 初始化慢指针 slow
        ListNode slow = head;
        // 初始化快指针 fast
        ListNode fast = head;
        
        // 循环条件:只要快指针及其下一个节点不为 null,就可以继续移动
        while (fast != null && fast.next != null) {
            // 慢指针每次移动一步
            slow = slow.next;
            // 快指针每次移动两步
            fast = fast.next.next;
            
            // 核心判断:如果快慢指针相遇,则证明存在环
            if (slow == fast) {
                return true;
            }
        }
        
        // 如果循环结束,说明快指针到达了链表末尾,不存在环
        return false;
    }
}

时空复杂度

时间复杂度:O(N)

  1. 无环情况 :如果链表没有环,快指针 fast 会先到达链表末尾。它走过的节点数大约是 N,而慢指针 slow 走了 N/2。总的来说,算法对链表进行了一次线性扫描。时间复杂度为 O(N)
  2. 有环情况
    • 设链表头到环入口的长度为 A,环的长度为 C
    • 当慢指针到达环入口时,它走了 A 步。此时,快指针已经在环内走了 A 步。
    • 此时,快指针相对于慢指针在环内的位置领先 A % C 步。
    • 慢指针需要被快指针追上。快指针每走一步,与慢指针的距离缩小 1。要追上 C - (A % C) 的距离,最多需要 C 步。
    • 因此,总的步数大约是 A (慢指针入环) + C (在环内追及),即 A+C。由于 A+C <= N,所以时间复杂度仍然是 O(N)

综合分析

无论链表是否有环,算法的时间复杂度都与链表的节点数 N 成线性关系,即 O(N)

空间复杂度:O(1)

  1. 主要存储开销 :该算法没有创建任何与输入规模 N 成比例的新的数据结构。
  2. 辅助变量 :只使用了 slowfast 两个指针变量来遍历链表。这些变量的数量是固定的,与链表长度无关。

综合分析

算法所需的额外辅助空间是常数级别的。因此,其空间复杂度为 O(1)。这是解决此问题最优的空间效率。

相关推荐
qq_5298353519 分钟前
ThreadLocal内存泄漏 强引用vs弱引用
java·开发语言·jvm
落笔画忧愁e24 分钟前
扣子Coze飞书多维表插件添加数据记录
java·服务器·飞书
黑听人2 小时前
【力扣 困难 C】329. 矩阵中的最长递增路径
c语言·leetcode
秋千码途3 小时前
小架构step系列08:logback.xml的配置
xml·java·logback
飞翔的佩奇3 小时前
Java项目:基于SSM框架实现的旅游协会管理系统【ssm+B/S架构+源码+数据库+毕业论文】
java·数据库·mysql·毕业设计·ssm·旅游·jsp
时来天地皆同力.3 小时前
Java面试基础:概念
java·开发语言·jvm
找不到、了4 小时前
Spring的Bean原型模式下的使用
java·spring·原型模式
阿华的代码王国4 小时前
【Android】搭配安卓环境及设备连接
android·java
铲子Zzz5 小时前
Java使用接口AES进行加密+微信小程序接收解密
java·开发语言·微信小程序