数据结构:如何判断一个链表中是否存在环(Check for LOOP in Linked List)

目录

初始思考:什么叫“链表有环”?

❌ 第一种直接想法(失败):我们是不是能“记住走过的节点”?

那我们换一个思路:我们能否只用两个指针来检测环?

第一步:定义两个指针

第二步:建立循环


初始思考:什么叫“链表有环”?

链表的本质是:

Node → Node → Node → NULL

但如果链表中某个节点的 next 指针不是 NULL,而是指向之前的某个节点:

Node → Node → Node  ↑         ↓  ←←←←←←←←←←

这就变成了一个“环形结构”。

❓我们怎么知道某个链表有没有环?

你无法一次看完所有节点。你只能从头开始,一个一个往后走:

while (p != NULL) {p = p->next;
}

正常情况下你最终会走到 p == NULL

但是如果链表有环,你会一直绕圈,永远也走不到 NULL,程序变成死循环。


❌ 第一种直接想法(失败):我们是不是能“记住走过的节点”?

想法:

每访问一个节点,就记住它;
如果某个节点我们第二次看到,就说明有环。

这个思路需要什么?
需要能“记住所有走过的节点”,并判断是否“已经访问过”

⚠️ 但我们没有 STL、没有哈希表、没有额外空间!

所以这个方法不符合第一性原则的最低资源要求,我们暂时排除。


那我们换一个思路:我们能否只用两个指针来检测环?

想象一下:

如果你在一个环形跑道上,有两个人:

  • 一个人慢跑(每次走一步)

  • 一个人快跑(每次走两步)

如果跑道没有环,快跑的人会直接跑出终点。

如果跑道是环形的,快跑的人迟早会“追上”慢跑的人!

这就是 Floyd’s Cycle Detection Algorithm(龟兔赛跑法)

我们现在从零构建它!


第一步:定义两个指针

slow = head;
fast = head;
  • slow 每次走一步:slow = slow->next

  • fast 每次走两步:fast = fast->next->next

 这一步是算法核心,“快指针”在追“慢指针” 

第二步:建立循环

变量含义
slow模拟“正常访问”的一步步访问
fast模拟“追赶检测”的两步步访问
slow==fast表示 fast 追上 slow,说明有环
fast==NULL表示链表已经结束,无环

问题:我们什么时候会出错?

  • 如果 fast == NULL:说明走到尾部

  • 如果 fast->next == NULL:再走一步就越界

我们必须确保两个指针不越界(避免访问 NULL 的 next):

while (fast != NULL && fast->next != NULL) {slow = slow->next;fast = fast->next->next;if (slow == fast) {// 二者相遇,说明有环}
}

❓为什么 slow 和 fast 相遇就说明有环?

因为只有在环中,快指针才有可能“绕回来追上慢指针”

就像操场上,跑得快的人终将追上慢跑的那个人。

如果没有环,fast 最终会变成 NULL 或 fast->next == NULL,循环终止。

如果链表中有环,两个指针最终会在环中某处相遇:

if (slow == fast)return 1;  // 有环

空链表或一个节点链表怎么办?

  • 如果 head == NULL:直接退出循环,返回 0 ✅

  • 如果只有一个节点:fast->next == NULL,也会直接退出 ✅

 完整代码

int hasLoop(struct Node* head) {struct Node* slow = head;struct Node* fast = head;// Step 1: 同时从头开始,每次走一步和两步while (fast != NULL && fast->next != NULL) {slow = slow->next;             // 慢指针走一步fast = fast->next->next;       // 快指针走两步if (slow == fast)              // 相遇 → 有环return 1;}// Step 2: 如果快指针走到了链表尾部,说明无环return 0;
}

时间 & 空间复杂度分析

复杂度说明
时间复杂度O(n),最坏情况 fast 会走到链表尽头(或追上 slow)
空间复杂度O(1),只用了两个指针变量,无需额外空间

我们从“什么都不会”出发,只知道我们:

  • 只能走 ->next

  • 不允许记录历史

  • 想办法“检测重复访问”

最终我们想到“相遇 → 有环”的思路,从而构建出用两个指针的检测算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917624.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917624.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解Java的SPI机制,使用auto-service库优化SPI

文章目录一、简介二、使用1、服务提供者(或者第三方公共):定义接口2、服务提供者:定义实现类3、服务提供者:注册服务4、构建服务提供者jar包5、客户端:使用 ServiceLoader 来加载服务三、源码分析1、源码2、…

PPT自动化 python-pptx - 10 : 表格(tables)

在日常工作中,我们经常需要制作包含表格的 PowerPoint 演示文稿,以此清晰展示数据或文本信息。手动制作不仅耗时,当数据更新时还需重复操作,效率低下。而 python-pptx 库为我们提供了自动化操作 PowerPoint 表格的可能。本文将详细…

在安卓中使用 FFmpegKit 剪切视频并添加文字水印

在安卓中用到的三方库:https://github.com/arthenica/ffmpeg-kit 这个库很强大,支持很多平台,每个平台都有各自的分支代码,用了一段时间,稳定性挺好的, 找到安卓下的分支:FFmpegKit for Andro…

Flask + HTML 项目开发思路

Flask HTML 项目开发思路:以公共资源交易信息展示为例 一、开篇明义——为什么选 Flask 框架 在众多 Python Web 框架(如 Django、Tornado 等)里,本次项目坚定选择 Flask,背后有清晰的技术考量: 1. 轻量…

Vue中:deep()和 ::v-deep选择器的区别

在 Vue.js 中,:deep()和 ::v-deep都是用于穿透组件作用域的深度选择器,但它们在语法、适用场景和版本支持上存在区别。以下是两者的核心差异:一、​​语法与用法​ :Vue2中用 ::v-deep,Vue2中不支持:deep()&#xff0c…

Deep learning based descriptor

1、DH3D: Deep Hierarchical 3D Descriptors for Robust Large-Scale 6DoF Relocalization 论文链接 代码链接 这是一篇训练点云的文章,在训练出local descriptor之后,通过聚类的方法得出global descriptor,并且提出了hierarchical network&…

PandasAI连接LLM对MySQL数据库进行数据分析

1. 引言 在之前的文章《PandasAI连接LLM进行智能数据分析》中实现了使用PandasAI连接与DeepSeek模型通过自然语言进行数据分析。不过那个例子中使用的是PandasAI 2.X,并且使用的是本地.csv文件来作为数据。在实际应用的系统中,使用.csv作为库表的情况比…

FloodFill算法——DFS

FloodFill算法就是用来寻找性质相同的连通快的算法,这篇博客都是用dfs来实现FloodFill算法 1.图像渲染 题目链接:733. 图像渲染 - 力扣(LeetCode) 题目解析:将和(sr,sc)相连的所有像素相同的…

【BUUCTF系列】[极客大挑战 2019]LoveSQL 1

本文仅用于技术研究,禁止用于非法用途。 Author:枷锁 文章目录一、题目核心漏洞分析二、关键解题步骤与技术解析1. 确定列数(ORDER BY)2. 联合查询获取表名3. 爆破字段名4. 提取Flag三、漏洞根源与防御方案1. 漏洞成因2. 防御措施四、CTF技巧…

AI时代,童装销售的“指路明灯”

别看现在AI、大数据这些词眼花缭乱的,当年我刚入行那会儿,也跟你一样,对着一堆库存和销量数据发愁,不知道劲儿该往哪使。童装销售这行,看着简单,其实水挺深。不过呢,这二十多年摸爬滚打下来&…

Swin-Transformer从浅入深详解

第一部分:出现背景在 Swin Transformer 出现之前,计算机视觉(Computer Vision, CV)领域主要由 CNN (卷积神经网络) 主导。后来,NLP(自然语言处理)领域的 Transformer 模型被引入 CV,…

如何手动打包 Linux(麒麟系统)的 Qt 程序

gcc版本 gcc版本确保目标系统(运行环境)的 GCC 版本 高于或等于开发环境的版本,否则程序无法在目标平台运行。通过 gcc -v 可查看当前版本。cmake生成可执行文件 强烈建议在cmakelists添加设置运行时 rpath 为 $ORIGIN/…/lib(相对…

解决 “crypto.hash is not a function”:Vite 从 6.x 升级至 7.x 后 `pnpm run dev` 报错问题

🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template 🌺 仓库主页: GitCode︱ Gitee ︱ Github 💖 欢迎点赞 👍 收藏 ⭐评论 …

我的创作纪念日____在 CSDN一年来的成长历程和收获

365 天创作札记:在代码与文字的褶皱里,遇见 1300 束光一年来。点开csdn网站后台粉丝数的那一刻,1327 这个数字在屏幕上微微发烫。原来那些在深夜敲下的字符、调试到凌晨的代码示例、反复修改的技术拆解,真的在时光里悄悄织成了一张…

VirtualBox 的 HOST 键(主机键)是 右Ctrl 键(即键盘右侧的 Ctrl 键)笔记250802

VirtualBox 的 HOST 键(主机键)是 右Ctrl 键(即键盘右侧的 Ctrl 键)笔记250802 VirtualBox 的 HOST 键(主机键)是什么?HOST键 是 右Ctrl 键VirtualBox 的 主机键(Host Key) 是一个…

Zama的使命

全同态加密(Fully Homomorphic Encryption,FHE)实现互联网端到端加密的使命的重要里程碑。(FHE) 是一种无需解密即可处理数据的技术。它可用于在公共、无需许可的区块链上创建私人智能合约,只有特定用户才能看到交易数据和合约状态…

Go语言流式输出技术实现-服务器推送事件(Server-Sent Events, SSE)

目录引言背景与技术概述实现技术细节1. HTTP 头部配置2. 事件格式与发送3. 保持连接与刷新4. 处理连接关闭4.1 使用上下文管理连接生命周期4.2 使用通道管理客户端连接5. 客户端交互6.demo7.Go转发大模型流式输出demo引言 服务器推送事件(Server-Sent Events, SSE&…

高端房产管理小程序

系统介绍1、用户端地图找房:对接地图API,地图形式显示周边房源,支持新盘和租房两种模式查询房价走势:城市房价走势,由后台每月录入房源搜索:搜索房源,支持多维度筛选房源类型:新盘销售、房屋租赁…

文本转语音(TTS)脚本

文本转语音(TTS)脚本 概述 generate_voice.py 是一个用于生成语音的Python脚本。该脚本提供了文本转语音(TTS)功能,可以将文本内容转换为语音文件。 功能特性 文本转语音: 将输入的文本转换为语音文件多种语音选项: 支持不同的语音类型和参数批量处理: 可以处理多个…

磁盘管理与分区

磁盘管理 一、磁盘类型 SATA,SCSI,SAS类型的磁盘,在Linux中用sd来表示。 其中第一块硬盘为sda,第二块二sdb,以此类推。 第一块硬盘的第一个分区为sda1。 nvme类型的磁盘,在Linux中使用nvmeXnYpZ进行表示。 X:数字&…