环形链表的进一步探究
茕茕白兔,东走西顾,衣不如新,人不如故
往期回顾:
数据结构——双向链表
数据结构——单链表
数据结构——顺序表
文章目录
如何判断一个链表是否为环形链表
环形链表的判断的深入探究
例1:沸羊羊追美羊羊
例2:灰太狼追懒羊羊
判断方法结论总结
环形链表的入口结点
源码及思路
另辟蹊径
大家好,我是纪宁。这篇文章将深入探究环形链表。
环形链表的形成是因为当单链表最后一个结点的指针域没有指向空,而指向了前面的任意一个结点。单链表链表一旦成环,在遍历的时候就会造成在一个区域里循环,那么对于环形链表的探究也应运而生。
在现实情况中,链表不可能这么短,所以如何判断链表是否成环,链表在何处成环,就成为了我们学习链表之后必须要研究的问题,也成为了面试题和笔试题中最喜欢考察的部分。
在上一篇链表刷题总结快慢指针的时候,博主曾粗略的谈到了环形链表的概念。这篇文章,博主带大家深入探究并总结,也算是博主自我内核的进一步提升吧。上篇文章的链接:快慢指针
如何判断一个链表是否为环形链表
首先,要明白环形链表的特征,如果定义一个指针去维护环形链表,它会在特定的位置进入环形链表内部,然后一直在环形链表内部循环遍历,这里如果使用快慢指针的话,在思路上是比较好理解的:当一个快指针和一个慢指针一起从头结点往后走,如果链表存在环,那么快指针首先进入环中,而慢指针则后入环,快指针在一直绕环循环的时候,一定能追上慢指针。如果没有追上的话,说明快指针一直向后走,直到 NULL,则说明这个链表不是环形指针。
代码如下
bool hasCycle(struct ListNode *head) {struct ListNode *slow=head;struct ListNode *fast=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;if(fast==slow)return true;}return false;
}
此代码将快指针定义为 fast ,慢指针定义为 slow,快指针每次向连链表末走两格,慢指针每次向链表末走一格(这里的走几格意思就是指针指向当前结点的后第几个结点),当两个指针相遇的时候,就说明这个链表带环。
环形链表的判断的深入探究
上段文章提到,快指针每次走两格,慢指针每次走一格,这里我们不禁有一些思考:真的能恰好追上吗?不会正好错过了吗?当快指针每次走三格,慢指针每次走一格呢?分析下面这几个例子。
例1:沸羊羊追美羊羊
沸羊羊跑的比较快,那么在无止尽的环中,它和美羊羊真的能正好相遇吗?
假设当美羊羊进入环中的时候,早已进入环中的沸羊羊和美羊羊在圆周上的距离为 N,而沸羊羊一次走的距离为 2,美羊羊一次走的距离为 1,沸羊羊每次比美羊羊多走的距离为 1 。
走 1 次后,他们俩之间的距离为 N-1
走 2 次后,他们俩之间的距离为 N-2
......
走 N-2 次后,他们俩之间的距离为 2
走 N-1 次后,他们俩之间的距离为 1
走 N 次后,他们俩之间的距离就为0,沸羊羊成功完美追上了美羊羊。
即使追上了,美羊羊就会接受沸羊羊吗?(开个玩笑)
这个例子说明了,快指针每次走两格,慢指针每次每次走一格,确实能相遇,可以用来判断环形链表,那么如果快指针每次走三格呢?
例2:灰太狼追懒羊羊
众所周知,红太狼爱吃羊,却从来没吃到过,但她有一个最疼爱她的老公灰太狼。灰太狼每天日复一日的想要抓到羊讨老婆换新,其中他最爱抓的羊,也是他觉得最好抓的羊,就是懒洋洋。
灰太狼和懒洋洋同时出发,灰太狼每次走三格,懒洋洋每次走一格,他们能完美相遇吗?
当懒洋洋进入环中时,灰太狼和它的举例为 M,环的长度为C,而懒洋洋每次走的距离为 1,灰太狼每次走的距离为 3,灰太狼比懒洋洋每次多走2灰太狼能否和懒洋洋完美相遇并抓到懒洋洋呢?
这种情况就和上面沸羊羊追美羊羊不一样了,无论沸羊羊和美羊羊距离多远,每次距离缩小1,总有一天距离会变为0。但灰太狼每次和懒洋洋的距离缩小 2,是否还会出现一直完美错过的情况。
假设灰太狼和懒洋洋之间的距离 M 为偶数,那么之间每次的距离为
M-2
M-4
......
2
0
这种情况是一定能追上的。
那么如果他们之间的距离 M 为奇数,那么他们之间每次的距离为
M-2
M-2
......
3
1
-1
当距离为-1时,灰太狼跑到了懒洋洋前面,那么灰太狼就刚好错过了懒洋洋,要进行下一圈追击。并且追击的距离与环的周长C有关,因为这次要追击的距离为 C-1。
当 C-1 为偶数,即周长C为偶数时,则恰好可以追上懒洋洋,如果C-1 依然为奇数,即周长C为偶数时,那这次也追不上懒洋洋了。因为这次追击的距离为奇数,所以下次追击的距离依然为 C-1 为奇数,这就成了一个死循环,这种情况下灰太狼就永远也抓不到懒洋洋!
判断方法结论总结
当快指针每次走两步,慢指针每次走一步的时候,这两个指针一定能相遇,这种方法可以判断出链表是否为环形链表。
当快指针每次走三步,慢指针每次走一步的时候,这两个指针是否相遇得取决于进入环中时两指针的距离,当这个距离为偶数时,则一定能相遇;当这个距离为奇数时,还要看环的周长,如果环的周长为奇数时,则可以相遇,当环的长度为偶数时,则永远不能相遇。这种方法不一定能判断出链表是否为环形链表。
当快指针每次走四步时,会有更复杂的情况......
环形链表的入口结点
当我们判断出了一个链表是否为环形链表的时候,还会思考,那这个链表是在哪个结点开始进入环的呢?
从上面那个例子中可以得出,快指针每次走两格,慢指针每次走一格,快指针一定能追上慢指针,可以判断出链表是否带环。
继续说美羊羊和沸羊羊那个例子,从上面的例子可以得之,当美羊羊进入环中时,沸羊羊一定能在一圈之内追上美羊羊。
设环的周长为4,环前面的链表长度为 L ,沸羊羊和美羊羊的相遇点与入环点的距离为 X,因为沸羊羊在美羊羊入环前可能已经绕环好几圈了,就设沸羊羊在和美羊羊相遇前已经在环里走了N 圈。
沸羊羊的速度是美羊羊的 2 倍,所以他们走的距离也是 2 倍关系
美羊羊走的距离为
沸羊羊走的距离为
列出等式为
得出 L 的值为
化简得
(N-1)*C 为沸羊羊在环里走的整圈的路程,而 (C-X) 为环的长度减去环的入口点到环的相遇点之间的长度。这个公式说明了一件事,当两个速度相同的指针,一个从起点开始走,一个从相遇点开始走,他们一定会在环入口点相遇(可能相遇前一个指针已经绕环很多圈了)。
源码及思路
首先,要找到快慢指针相遇的点,如果快慢指针没有相遇的话,则说明这个链表没有环。其次,重新定义两个速度相同的指针,一个从快慢指针的相遇点开始绕着环走,一个指针从起点开始走,这两个指针相遇的地方,就是环的入口结点。
struct ListNode *detectCycle(struct ListNode *head) {struct ListNode *fast=head,*slow=head;while(fast&&fast->next){fast=fast->next->next;slow=slow->next;if(fast==slow)//相遇了{struct ListNode*cur1=head;struct ListNode*cur2=fast;while(cur1!=cur2){cur1=cur1->next;cur2=cur2->next;}return cur1;}}return NULL;
}
这是力扣上一道中等难度的题,但是只要分析思路正确了,写代码就是分分钟的问题。
另辟蹊径
第一种思路,在很多人看来,特别是第一次接触这道题的人,比较难理解一点,那这里还有一种思路较为简单的方法,但简单的思路通常伴随着较为复杂的代码,可以说各有取舍吧,大家按需选择。
在此之前,博主在这里再介绍一个经典题目:
判断两个链表是否为相交链表,并找出他们的第一个共同结点
相交链表,即两个链表有重叠的公共结点,他们一旦有公共结点重叠,那么这个结点后面的结点就一定会重合。
判断环形链表的思路是,两个链表各定义一个指针来维护,先遍历计算出链表的长度,顺便比较链表最后一个结点是否重合,如果不重合,则肯定不是相交结点。先让较长链表的指针向前走 差值 步,然后两个指针同时走,直到有一个相同的结点,这个结点就是相交链表的第一个共同结点。
回到刚开始的问题,如何另辟蹊径找到环形链表的入口呢?既然刚才将了相交链表的思路,那肯定一想就直到:转化为相交链表求。
如图,在找到相遇点后,将相遇点断开。红色部分为一个链表,绿色部分为一个链表,他们组成一个相交链表,相交链表的第一个结点就是环形链表的入环结点。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {int lenA=1,lenB=1;//求相交链表的第一个结点struct ListNode*curA=headA,*curB=headB;while(curA->next!=NULL){curA=curA->next;lenA++;}while(curB->next!=NULL){curB=curB->next;lenB++;}if(curA->val!=curB->val){return NULL;}int dis=abs(lenA-lenB);//求差值绝对值struct ListNode*longnode=headA,*shortnode=headB;if(lenB>lenA){longnode=headB;shortnode=headA;}while(dis--){longnode=longnode->next;}while(longnode!=NULL){if(longnode==shortnode){return longnode;}longnode=longnode->next;shortnode=shortnode->next;}return NULL;
}
struct ListNode *detectCycle(struct ListNode *head) {struct ListNode *slow=head;struct ListNode *fast=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;if(fast==slow)//求相遇点{struct ListNode *cur=fast->next;fast->next=NULL;return getIntersectionNode(cur,head);}}return NULL;
}
这种方法虽然思路简单,但代码比较长,代码变长,就容易出错,所以说每种方法都是各有利弊。
相关文章:

环形链表的进一步探究
茕茕白兔,东走西顾,衣不如新,人不如故 往期回顾: 数据结构——双向链表 数据结构——单链表 数据结构——顺序表 文章目录 如何判断一个链表是否为环形链表 环形链表的判断的深入探究 例1:沸羊羊追美羊羊 例…...
flink任务性能优化
1、使用异步算子,异步执行操作 2、将下游数据需要的数据以参数的形式向下传递 3、当服务器资源有限的情况下,慎用RocksDBStateBackend RocksDBStateBackend performance will be poor because of the current Flink memory configuration! RocksDB wi…...

vue2 el-carousel轮播图和文字一起改变
vue项目的话 安装一下element依赖 npm i element-ui -S在main入口文件引入element包 我在app文件里边去写的 <template><div class"w"><el-carousel height"460px"><el-carousel-item v-for"item in items" :key"i…...
LangChain:打造自己的LLM应用 | 京东云技术团队
1、LangChain是什么 LangChain是一个框架,用于开发由LLM驱动的应用程序。可以简单认为是LLM领域的Spring,以及开源版的ChatGPT插件系统。核心的2个功能为: 1)可以将 LLM 模型与外部数据源进行连接。 2)允许与 LLM 模…...

字节跳动测试岗,3面都过了,HR告诉我这个原因被刷了...
说在前面 面试时最好不要虚报工资。本来字节跳动是很想去的,几轮面试也通过了,最后没offer,自己只想到下面几个原因: 虚报工资,比实际高30%; 有更好的人选,这个可能性不大,我看还在…...

Android 14重要更新预览
Android 14重要更新预览 国际化 Android 14 在 Android 13 的基础上进一步扩展了按应用设定语言功能,提供了一些额外的功能: 自动生成应用的 localeConfig:从 Android Studio Giraffe Canary 7 和 AGP 8.1.0-alpha07 开始,您可以…...

快速上手字符串函数
文章目录 前言一、求字符串的长度strlen函数strlen函数学习使用strlen函数模拟实现strlen函数模拟实现方法1:计数器法strlen函数模拟实现方法2:指针减指针法strlen函数模拟实现方法3:递归方法 二、字符串的拷贝,拼接和比较strcpy函…...

linux(centos) docker 安装 nginx
1、拉取nginx最新版本镜像 docker pull nginx:latest 查看镜像 docker images 或者 docker images -a 2.启动nginx容器 docker run -d -p 80:80 --name nginx nginx 使用docker run命令,启动nginx容器。 --name,设置容器名。为方便记忆ÿ…...
SpringBoot 整合 Minio
官网: MinIO 是一个基于 Go 实现的高性能、兼容 S3 协议的对象存储。它采用 GNU AGPL v3 开源协议,项目地址是 https://github.com/minio/minio 。 它适合存储海量的非结构化的数据,例如说图片、音频、视频等常见文件,备份数据、…...

《吐血整理》高级系列教程-吃透Fiddler抓包教程(24)-Fiddler如何优雅地在正式和测试环境之间来回切换-中篇
1.简介 在开发或者测试的过程中,由于项目环境比较多,往往需要来来回回地反复切换,那么如何优雅地切换呢?宏哥今天介绍几种方法供小伙伴或者童鞋们进行参考。 2.实际工作场景 2.1问题场景 (1)已发布线上…...

探索 GPTCache|GPT-4 将开启多模态 AI 时代,GPTCache + Milvus 带来省钱秘籍
世界正处于数字化的浪潮中,为了更好理解和分析大量数据,人们对于人工智能(AI)解决方案的需求呈爆炸式增长。 此前,OpenAI 推出基于 GPT-3.5 模型的智能对话机器人 ChatGPT,在自然语言处理(NLP&a…...

纯css实现登录表单动效
效果图: 代码展示 // 我这边用的是elementUI表单校验,更改的样式。 <el-form:model"form":rules"rules"ref"fromList":hide-required-asterisk"true"><el-form-item prop"account"><…...

【css】外边距margin
外边距中有一个属性值比较有意思:inherit 值,继承父类的属性。 <!DOCTYPE html> <html> <head> <style> div {border: 1px solid red;margin-left: 100px; }p.ex1 {margin-left: inherit; } </style> </head> <…...

Cpp8 — 二叉搜索树
二叉搜索树(搜索二叉树、二叉排序树) 二叉搜索树又称二叉排序树,它要么是一棵空树,要么是具有以下性质的二叉树: 1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 2.若它的右子树不为空&…...

【实操教程】如何开始用Qt Widgets编程?(一)
Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。 在本文中࿰…...

openmp和avx配置
实际场景: 项目中数据拷贝慢(使用的是memcpy),希望能加速拷贝,所以尝试了使用avx的流方式,和openmp方式处理 问题1: 调用avx是报错 error: inlining failed in call to always_inline ‘__m512…...
18 个JS优化技巧,可以解决 90% 的屎山代码!!!
文章目录 18 个JS优化技巧,可以解决 90% 的屎山代码!!!1.箭头函数2.解构赋值变量3.使用模版字面量进行字符拼接4.使用展开运算符进行数组和对象操作5.简化循环6.简化判断7.使用对象解构和默认参数简化函数参数8.使用函数式编程概念…...

go逆向符号恢复
前言 之前一直没怎么重视,结果发现每次遇到go的题都是一筹莫展,刷几道题练习一下吧 准备 go语言写的程序一般都被strip去掉符号了,而且ida没有相关的签名文件,没办法完成函数名的识别与字符串的定位,所以第一步通常…...

论文阅读- Uncovering Coordinated Networks on Social Media:Methods and Case Studies
链接:https://arxiv.org/pdf/2001.05658.pdf 目录 摘要: 引言 Methods Case Study 1: Account Handle Sharing Coordination Detection 分析 Case Study 2: Image Coordination Coordination Detection Analysis Case Study 3: Hashtag Sequen…...
应急响应-Linux
应急响应-Linux 1.关键目录 /etc/passwd 记录用户信息 /etc/shadow 保存用户密码(hash) /etc/crontab 定时任务文件 /etc/anacrontab 异步定时任务文件 /etc/rc.d/rc.local 开机启动项 /var/log/btmp …...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...

springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...
LangChain【6】之输出解析器:结构化LLM响应的关键工具
文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器?1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...

高抗扰度汽车光耦合器的特性
晶台光电推出的125℃光耦合器系列产品(包括KL357NU、KL3H7U和KL817U),专为高温环境下的汽车应用设计,具备以下核心优势和技术特点: 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计,确保在…...