当前位置: 首页 > news >正文

【LeetCode每日一题】141. 环形链表 142.环形链表 II

每日一题

  • 141. 环形链表
    • 题目
    • 总体思路
        • 快慢指针工作原理
        • 数学证明
        • 时间复杂度与空间复杂度
    • 代码
  • 142. 环形链表 II
    • 题目
    • 总体思路
      • 算法步骤
        • 第一阶段:检测环的存在
        • 第二阶段:定位环起点
      • 时间复杂度与空间复杂度
    • 代码
    • 小感悟

2025.8.29

141. 环形链表

题目

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:
在这里插入图片描述
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:
在这里插入图片描述
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:
在这里插入图片描述
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

总体思路

快慢指针工作原理

这个算法使用Floyd判圈算法(龟兔赛跑算法):

  1. 两个指针:慢指针(每次1步)和快指针(每次2步)
  2. 无环情况:快指针会先到达链表末尾(nil)
  3. 有环情况:快指针最终会追上慢指针(在环内相遇)
数学证明

假设:

  • 链表头部到环起点的距离:m
  • 环的长度:n
  • 快指针速度是慢指针的2倍

当慢指针进入环时,快指针已经在环中。由于速度差为1,快指针最多需要n步就能追上慢指针。

时间复杂度与空间复杂度
  • 时间复杂度:O(n)
    • 最坏情况下需要遍历整个链表
    • 有环时会在O(n)时间内检测到
  • 空间复杂度:O(1)
    • 只使用了两个指针变量
    • 不需要额外数据结构

代码

golang

/*** Definition for singly-linked list.* type ListNode struct {*     Val int*     Next *ListNode* }*/
func hasCycle(head *ListNode) bool {// 如果链表为空或者只有一个节点,肯定没有环if head == nil || head.Next == nil {return false}// 初始化快慢指针,都指向头节点slow := headfast := head// 遍历链表,检测是否有环// 条件:fast和fast.Next都不为nil,确保可以安全移动for fast != nil && fast.Next != nil {slow = slow.Next      // 慢指针移动一步fast = fast.Next.Next // 快指针移动两步// 如果快慢指针相遇,说明有环if slow == fast {return true}}// 如果快指针到达链表末尾,说明没有环return false
}
func hasCycle(head *ListNode) bool {if head == nil || head.Next ==nil {return false}slow := headfast := headfor fast != nil && fast.Next != nil {fast = fast.Next.Nextslow = slow.Nextif fast == slow {return true}}return false
}

142. 环形链表 II

题目

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:
在这里插入图片描述
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:
在这里插入图片描述
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:
在这里插入图片描述
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105
pos 的值为 -1 或者链表中的一个有效索引

进阶:你是否可以使用 O(1) 空间解决此题?

总体思路

这个算法采用Floyd判圈算法(龟兔赛跑算法)来检测链表中的环并定位环的起始节点。算法分为两个阶段:

  1. 检测阶段:使用快慢指针判断链表是否有环
  2. 定位阶段:如果有环,找到环开始的位置

算法步骤

第一阶段:检测环的存在
  • 慢指针每次移动1步,快指针每次移动2步
  • 如果快慢指针相遇,说明有环
  • 如果快指针到达链表末尾,说明无环
第二阶段:定位环起点
  • 将慢指针重置到链表头部
  • 快慢指针都以每次1步的速度移动
  • 再次相遇的点就是环的起始节点

时间复杂度与空间复杂度

  • 时间复杂度:O(n)
    • 检测阶段最多遍历n个节点
    • 定位阶段最多遍历n个节点
    • 总计:O(n) + O(n) = O(n)
  • 空间复杂度:O(1)
    • 只使用了2个指针变量
    • 不需要额外的数据结构

代码

golang

/*** 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}panduan := falseslow := headfast := headfor fast != nil && fast.Next != nil {slow = slow.Nextfast = fast.Next.Nextif slow == fast {panduan = truebreak}}if !panduan {return nil}slow = headfor slow != fast {slow = slow.Nextfast = fast.Next}return slow
}
/*** 检测链表中的环并返回环的起始节点* 使用Floyd判圈算法(龟兔赛跑算法)* * @param head *ListNode 链表的头节点* @return *ListNode 环的起始节点,如果没有环返回nil*/
func detectCycle(head *ListNode) *ListNode {// 边界条件处理:// 1. 空链表肯定没有环// 2. 单节点链表(没有自环)也没有环if head == nil || head.Next == nil {return nil}// 初始化标志变量,用于记录是否检测到环hasCycle := false// 初始化快慢指针,都指向链表头节点slow := head  // 慢指针,每次移动1步fast := head  // 快指针,每次移动2步// 第一阶段:检测链表是否有环// 循环条件:确保fast和fast.Next不为nil,避免空指针异常for fast != nil && fast.Next != nil {// 慢指针移动1步slow = slow.Next// 快指针移动2步(这是算法的关键)// 快指针速度是慢指针的2倍,确保在有环情况下会相遇fast = fast.Next.Next// 如果快慢指针相遇,说明链表有环if slow == fast {hasCycle = true  // 设置标志位break           // 跳出循环}}// 如果没有检测到环,直接返回nilif !hasCycle {return nil}// 第二阶段:定位环的起始节点// 将慢指针重新指向链表头节点slow = head// 快慢指针都以每次1步的速度移动// 根据数学推导,它们会在环的起始节点相遇for slow != fast {slow = slow.Next  // 慢指针移动1步fast = fast.Next  // 快指针移动1步}// 返回环的起始节点(此时slow和fast指向同一个节点)return slow
}

小感悟

这两个环形链表用快慢指针好方便!
但是我还不会用别的方法,先学会这一种算了!

http://www.dtcms.com/a/356875.html

相关文章:

  • 麒麟系统使用-VSCode运行.net过程中一些可能问题及解决办法
  • 【前端教程】JavaScript 对象与数组操作实战:从基础到优化
  • 课程视频怎么加密?在线教育机构常用的6个课程加密方法
  • 视频转音频
  • 学习Java30天(tcp的多开客户端和bs架构以及java高级)
  • R 语言 + 卒中 Meta 分析
  • 如何用 Kotlin 在 Android 手机开发一个小闹钟、计时器、秒表
  • Vue3+Ant-design-vue 实现树形穿梭框
  • Java中对泛型的理解
  • mes表结构思维导图
  • 基于机器学习的多个模型的预测Backtrader自动化交易系统设计
  • Java设计模式是什么?核心设计原则有哪些?
  • 编程速递:RAD Studio 13 即将到来的功能
  • Linux应用软件编程--->数据库
  • C++函数继承
  • 【C++闯关笔记】STL:vector的学习与使用
  • 论文阅读:ICLR 2024 GAIA: A Benchmark for General AI Assistants
  • DBeaver中禁用PostgreSQL SSL的配置指南
  • SQL Server 查看备份计划
  • Creed —— 设置玩家属性(生命/耐力/经验值等)
  • 初学python的我开始Leetcode题-17
  • Azure Marketplace 和 Microsoft AppSource的区别
  • 订餐后台管理系统 -day03 登录模块
  • Linux操作系统Shell脚本-第一章
  • 数据防泄与最小可见:ABP 统一封装行级安全(RLS)+ 列级脱敏
  • 前端vue3入门学习
  • 数据分析编程第七步:分析与预测
  • 【MFC自动生成的文件详解:YoloClassMFC.cpp 的逐行解释、作用及是否能删除】
  • 科技赋能医疗:陪诊小程序系统开发,让就医不再孤单
  • cursor的setting設置換行