每日一题---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.hqwc.cn/news/615806.html

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

相关文章

全新4.0版本圈子社交论坛系统 ,可打包小程序,于TP6+uni-app 全开源 可打包小程序app uniapp前端+全开源+独立版

简述 首先 圈子系统的核心是基于共同的兴趣或爱好将用户聚集在一起,这种设计使得用户能够迅速找到与自己有共同话题和兴趣的人。 其次 圈子系统提供了丰富的社交功能,如发帖、建圈子、发活动等,并且支持小程序授权登录、H5和APP等多种形式…

最齐全,最简单的免费SSL证书获取方法——实现HTTPS访问

一:阿里云 优势:大平台,在站长中知名度最高,提供20张免费单域名SSL证书 缺点:数量有限,并且只有单域名证书,通配符以及多域名没有免费版本。并且提供的单域名证书只有三个月的期限。 二&#…

【蓝桥杯】蓝桥杯算法复习(五)

😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!&#x1f4…

Leetcode刷题之合并两个有序数组

Leetcode刷题之合并两个有序数组 一、题目描述二、题目解析 一、题目描述 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数…

使用Vivado Design Suite进行功率优化

功率优化是一个可选步骤,它通过使用时钟门控来优化动态功率。它既可以在Project模式下使用,也可以在Non-Project模式下使用,并且可以在逻辑优化之后或布局之后运行,以减少设计中的功率需求。功率优化包括Xilinx的智能时钟门控解决…

【零基础学数据结构】双向链表

1.双向链表的概念 1.1头节点 1.2带头双向循环链表 注意: 哨兵位创建后,首尾连接自己 1.3双链表的初始化 // 双向链表的初始化 void ListInit(ListNode** pphead) {// 给双链表创建一个哨兵位*pphead ListBuyNode(-1); } 2.双向链表的打印 // 双向…

HarmonyOS开发实例:【app帐号管理】

应用帐号管理 介绍 本示例选择应用进行注册/登录,并设置帐号相关信息,简要说明应用帐号管理相关功能。效果图如下: 效果预览 使用说明参考鸿蒙文档:qr23.cn/AKFP8k点击或者转到。 1.首页面选择想要进入的应用,首次进…

Redis 之集群模式

一 集群原理 集群,即Redis Cluster,是Redis 3.0开始引入的分布式存储方案。 集群由多个节点(Node)组成,Redis的数据分布在这些节点中。 集群中的节点分为主节点和从节点:只有主节点负责读写请求和集群信息的维护;从…

接口测试用例编写和接口测试模板

一、简介 接口测试区别于传统意义上的系统测试,下面介绍接口测试用例和接口测试报告。 二、接口测试用例模板 功能测试用例最重要的两个因素是测试步骤和预期结果,接口测试属于功能测试,所以同理。接口测试的步骤中,最重要的是将…

项目管理软件评测:选择合适软件是关键

在过去,中小企业项目管理沿用的是office全家桶,用到后面项目由简单变复杂,项目资源越来越庞大,项目成员越来越多,项目管理问题日益凸显。好用的项目管理软件是化解问题的好方法,好用的项目管理软件是什么样…

(Oracle)SQL优化案例:隐式转换优化

项目场景 项目现场的某个kettle模型执行非常缓慢,原因在于某个SQL执行效率非常的低。甲方得知此事要求公司赶紧优化,负责该模块的同事对SQL优化并不熟悉。所以作为一个立志成为优秀DBA的ETL工程师,我自告奋勇:不是DBA,…

4.2 面向对象程序设计-类的继承实验

本文仅供学习交流,严禁用于商业用途,如本文涉及侵权请及时联系将于24小时内删除 目录 1.实验内容 2.实验原理 2.1类的继承 2.2 继承的优点和缺点 2.3 继承的方式 3.实验代码 1.实验内容 创建一个父类CalcTime,在父类中依次定义用于保存…