每日一题---OJ题: 环形链表 II

片头

嗨! 小伙伴们,大家好! 我们又见面啦,在上一篇中,我们学习了环形链表I, 今天我们继续来打boss,准备好了吗? Ready Go ! ! ! 

emmm,同样都是环形链表,有什么不一样的地方呢?

肯定有, 要不然也不会一个标记为"简单" ,一个标记为"中等"了,哈哈哈哈哈

让我们分析一下题目,哦~,原来是要返回链表开始入环的第一个结点呀! 上一个题目只是让我们简单的判断一下链表中是否有环,看来这次要求比上一次更高了,不怕不怕,一起来做一做~~~

举个例子:

在上图中,总共有4个结点,分别为 3, 2, 0, -4, 链表中有一个环,尾结点连接到第二个结点,返回第二个结点。

上图中,总共有2个结点, 分别为 1, 2, 链表中有一个环,尾结点连接到第一个结点,返回第一个结点。

上图中,只有1个结点,链表没有环,返回NULL

针对这道题,有两种思路,我们一起来看看吧!

思路一:  我们定义两个指针,分别为 fast 和 slow指针, slow指针每次走1步,fast指针每次走2步,利用 fast 和 slow 之间的数学逻辑关系,来解答此题。

当slow指针走到中间的时候,fast指针开始进环

链表头->入口点: L

当slow指针开始进环的时候,fast指针在环中可能已经走了n圈了

 此时,fast指针和slow指针都在环里面,slow指针进环以后开始追击。在上一章中,我们知道,当fast和slow之间的速度只相差1个单位的时候,fast指针一定能追上slow,最终两指针相遇。

链表头->入口点: L

入口点->相遇点: X

环的长度: C

追上相遇时: slow走的距离: L+X

追上相遇时: fast走的距离: L+ n*C + X  (假设fast追上slow时,转了n圈 (n>=1))

思考: slow有没有可能转了超过1圈? 没有! 因为slow都走了一圈了,那么fast走了2圈了,早追上了。

fast 追上slow时,fast走的距离是slow的2倍,我们可以列一个等式:

                      2*(L+X) = L + n*C + X

                            L+X = n*C 

                               L = n*C - X

哈哈哈,重头戏来咯! 我们可以得到一个结论: 一个指针从链表头开始走,一个指针从相遇点开始走,它们都以相同的速度(每次一步)进行移动,最终它们会在入口点相遇。

所以,基于这个结论,这道题的代码如下:

  typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {ListNode* fast = head;      //初始时,定义一个fast指针指向头结点ListNode* slow = head;      //初始时,定义一个slow指针指向头结点while(fast && fast->next)   //当fast为空或者fast->next为空,则跳出循环{fast = fast->next->next;//fast每次走2步slow = slow->next;      //slow每次走1步if(fast == slow)        //如果fast和slow相遇{ListNode* meet = slow;//定义一个meet结点,用来找入口点while(meet != head)   //只有当两个指针再次相遇,循环才结束{meet = meet->next;//meet每次走一步head = head->next;//head每次走一步}return meet;          //当两个指针再次相遇时,即入环的第一个结点}}return NULL;            //如果fast为空或者fast->next为空,说明链表里面没有环
}

emmm,有的小伙伴会这样说: 哎呀,我想不起来这个推导过程以及结论,有没有其他的思路?

当然有! 且听我慢慢道来~

思路二:  我们定义两个指针,分别为 fast 和 slow指针, slow 指针每次走1步, fast 指针每次走2步, 当它们第一次相遇的时候,我们就把这个环断开,变成两个单链表,查找相交结点。

嗯,有点不太好理解,咱们画个图吧!

上图中,总共有8个结点,分别为 a1, a2, b1, b2, b3, c1, c2, c3 ,链表中有一个环,由 c3 结点指向 b1 结点, 题目要求我们把第一个相交结点(也就是 c1 结点)返回。

同理,我们先让fast指针每次走2步,slow指针每次走1步,它们最终会在 b2 结点相遇。

第一次:

第二次:

第三次:

第四次:

第五次:

第六次:

当快慢指针第一次相遇时,我们用meet结点来代表它们相遇的结点。用meet结点next指针(meet->next)指向headx结点,随后meet结点next指针指向NULL。

也就变成了这样:

变成了2个单链表, 分别是 a1->a2->c1->c2->c3->b1->b2b3->c1->c2->c3->b1->b2

现在我们需要求两个单链表第一次相交的结点,哈哈哈,求链表开始入环的第一个结点瞬间变成了求两个单链表的第一次相交的交点,好神奇~

注意: 小伙伴们如果不会求两个链表相交的起始结点,可以参考这篇文章相交链表(点击蓝色字体可以跳转相应的文章)

整体代码如下:

   typedef struct ListNode ListNode;struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB){ListNode* pA = headA;   //pA指针指向头结点(链表头)ListNode* pB = headB;   //pB指针指向头结点(链表头)int lenA = 0;           //统计链表A的长度while(pA->next != NULL) //查找链表A的尾结点{pA = pA->next;      //pA会一直遍历到尾结点      lenA++;             //每走过一个结点,链表A的长度自增一次        }lenA = lenA + 1;        //不要忘了尾结点int lenB = 0;           //统计链表B的长度while(pB->next != NULL) //查找链表B的尾结点{pB = pB->next;      //pB会一直遍历到尾结点      lenB++;             //每走过一个结点,链表A的长度自增一次        }lenB = lenB + 1;         //不要忘了尾结点if(pA != pB){            //如果尾结点不相同,说明链表A和链表B根本不会相交return NULL;         //返回NULL}//求链表A和链表B的长度差int gap =abs(lenA-lenB);       //假设链表A的长度比链表B短,那么将A链表定义为shortListListNode* shortList = headA; //假设链表B的长度比链表A长,那么将B链表定义为longList   ListNode* longList = headB;   if(lenA > lenB){                               //如果链表A的长度大于链表BshortList = headB;          //将链表B定义为shortListlongList = headA;           //将链表A定义为longList}while(gap--){                   //让长的链表先走长度差longList = longList->next;  }//现在长的链表和短的链表起始位置和尾结点的距离相同//让他们每一个结点依次比较,直到找到第一个相同的结点为止(也就是交点)while(shortList != longList)    {longList = longList->next;shortList = shortList->next;}return shortList;   //找到交点了,将这个结点返回}struct ListNode *detectCycle(struct ListNode *head) {ListNode* fast = head;                //定义一个fast指针,指向链表头ListNode* slow = head;                //定义一个slow指针,指向链表头while(fast && fast->next)             //当fast为空或者fast->next为空时,退出循环{fast = fast->next->next;         //fast指针每次走2步 slow = slow->next;               //slow指针每次走1步if(fast == slow)                 //如果fast指针和slow指针第一次相遇{//定义一个变量meet,用来保存它们第一次相遇的结点ListNode* meet = slow;   //定义一个变量headx,将meet的next指针指向headxListNode* headx = meet->next;//将meet结点的next指针置为NULLmeet->next = NULL;        return getIntersectionNode(head,headx);//将找到的相交结点返回}}return NULL;  //如果fast为空,或者fast->next为空,说明链表没有成环,返回NULL
}

片尾

今天我们学习了一道OJ题---环形链表II ,这道题是在上一篇(环形链表)的基础上,难度稍微提高,不仅需要我们判断链表中是否存在环,如果链表中成环,我们还需要求出入环的第一个结点并返回,但是,这道题如果仔细想一想,还是可以做出来,希望看完这篇文章能对友友们有所帮助 !   !   !

点赞收藏加关注 !   !   !

谢谢大家 !  !  !

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

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

相关文章

·13·1dawwd

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话: 知不足而奋进,望远山而前行&am…

轮腿机器人-五连杆正运动学解算

轮腿机器人-五连杆与VMC 1.五连杆正运动学分析2.参考文献 1.五连杆正运动学分析 如图所示为五连杆结构图,其中A,E为机器人腿部控制的两个电机,θ1,θ4可以通过电机的编码器测得。五连杆控制任务主要关注机构末端C点位置,其位置用直…

Zotero插件ZotCard中AI-NNDL文献笔记卡分享及卡片使用方法

一、卡片社区分享 github:ZotCard插件AI-NNDL论文卡片模板 Issue #67 018/zotcard (github.com) 二、卡片效果预览 ZotCard插件AI-NNDL论文卡片模板是关于人工智能神经网络与深度学习论文的笔记卡片,效果预览如下图: 三、卡片代码 经过了…

SAP CAP篇十七:写个ERP的会计系统吧,Part IV

本文目录 本系列文章目标开发步骤数据库表设计借贷初始化数据 会计凭证 Service 定义生成Fiori App更新CDS AnnotationApp运行 本系列文章 SAP CAP篇一: 快速创建一个Service,基于Java的实现 SAP CAP篇二:为Service加上数据库支持 SAP CAP篇三&#xff…

HarmonyOS分布式应用框架深入解读

随着越来越多设备的智能化,在多设备场景下应用开发面临以下挑战:从多设备的形态差异(不同大小、不同分辨率、不同形状的屏幕,多样化的交互方式–按钮、触屏、键盘、语音、手势等),多设备的能力差异&#xf…

AI赋能档案开放审核:实战

关注我们 - 数字罗塞塔计划 - 为进一步推进档案开放审核工作提质增效,结合近几年的业务探索、研究及项目实践,形成了一套较为成熟、高效的AI辅助档案开放审核解决方案,即以“AI人工”的人机协同模式引领档案开放审机制创新,在档…

一站式开源持续测试平台 MerterSphere 之测试跟踪操作详解

一、MeterSphere平台介绍 MeterSphere是一站式的开源持续测试平台,遵循 GPL v3 开源许可协议,涵盖测试跟踪、接口测试、UI 测试和性能测试等功能,全面兼容JMeter、Selenium 等主流开源标准,有效助力开发和测试团队充分利用云弹性…

TCP协议简单总结

TCP:传输控制协议 特点:面向连接、可靠通信 TCP的最终目的:要保证在不可靠的信道上实现可靠的传输 TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接 三次握手建立可靠…

Golang ProtoBuf 初学者完整教程:语法

一、编码规范推荐 1、文件名使用小写下划线的命名风格,例如 lower_snake_case.proto 2、使用 2 个空格缩进 3、包名应该和目录结构对应 4、消息名使用首字母大写驼峰风格(CamelCase),例如message StudentRequest { ... } 5、字段名使用小写下划线的风格…

【系统分析师】操作系统部分

文章目录 1、进程状态2、前趋图3、PV操作4、死锁问题5、存储管理5.1 页式存储5.2 段式存储5.3 段页式存储5.4 页面置换算法 6、文件管理6.1 索引文件结构6.2 空闲存储空间管理 7、设备管理7.1数据传输控制7.2 虚设备和SPOOLING技术7.3 微内核操作系统7.4 嵌入式操作系统 说明&a…

LeetCode-32. 最长有效括号【栈 字符串 动态规划】

LeetCode-32. 最长有效括号【栈 字符串 动态规划】 题目描述:解题思路一:辅助栈解题思路二:动态规划解题思路三:0 题目描述: 给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且…

「51媒体-邀约媒体」活动发布会新闻通稿如何写?

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 撰写活动发布会的新闻通稿需要遵循一定的结构和内容要点,以确保信息的准确性、完整性和吸引力。以下是撰写活动发布会新闻通稿的基本步骤和建议: 标题&#xff1…

初识 QT

初始QT 什么是QTQT发展史QT支持的平台QT的优点QT的应用场景搭建QT开发环境QT的开发工具概述QT下载安装 使用QT创建项目QT 实现Hello World程序使用按钮控件来实现使用标签控件来实现 项目文件解析widget.hmain.cppwidget.cppwidget.ui.pro文件 对象树QT 窗口坐标体系 什么是QT …

python linux服务器ssh简单爆破(测试用户名密码)(连接ssh服务器)(测试登录ssh服务器)

文章目录 背景示例代码代码解释导入模块SSH服务器的地址和端口用户名和密码列表生成所有可能的用户名和密码组合尝试连接到SSH服务器并验证用户名和密码遍历并测试每一对凭证 背景 我们华为摄像头linux终端的密码忘了,还不太好初始化,手动一个个测试太麻…

【QingHub】EMQX单节点一键部署

EMQX 简介 EMQX是全球最具扩展性的开源MQTT 代理,具有高性能,可在 1 个集群中连接 1 亿多个 IoT 设备,同时保持每秒 100 万条消息的吞吐量和亚毫秒级的延迟。 EMQX 支持MQTT、HTTP、QUIC、WebSocket等多种开放标准协议。它 100% 符合MQTT 5.…

ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写

2022年11月30日,可能将成为一个改变人类历史的日子——美国人工智能开发机构OpenAI推出了聊天机器人ChatGPT3.5,将人工智能的发展推向了一个新的高度。2023年4月,更强版本的ChatGPT4.0上线,文本、语音、图像等多模态交互方式使其在…

基于Springboot+Vue的Java项目-在线视频教育平台系统(附演示视频+源码+LW)

大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &am…

使用 Python 标记具有相同名称的条目

如果大家想在 Python 中标记具有相同名称的条目,可以使用字典(Dictionary)或集合(Set)来实现。这取决于你们希望如何存储和使用这些条目。下面我将提供两种常见的方法来实现这个目标。 1、问题背景 在处理数据时&…

电脑端微信截图文字识别功能效率更高了

近期发现微信中的截图文字识别比QQ中的截图文字识别效率高更高,效果更好。 使用方法: 安装电脑端微信客户端:https://weixin.qq.com/(如果没有下载,可以安装一下) 默认截图组合快捷键是:ALTA (使用下来感觉不是很顺手…

AI在运维实践中的价值提升

在2024年的AI赛道上,利用大数据 、机器学习算法、人工智能来改善运维效率已成为软件运营商发展的新主张,通过AI在运维流程的洞察、决策和执行,从而提升效率、减少故障时间,优化用户体验。通过分析大量数据来识别趋势和模式&#x…