每日一题---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链表定义为shortList
        ListNode* shortList = headA; 
      //假设链表B的长度比链表A长,那么将B链表定义为longList   
        ListNode* longList = headB;   
        if(lenA > lenB)
        {                               //如果链表A的长度大于链表B
            shortList = headB;          //将链表B定义为shortList
            longList = 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指针指向headx
                ListNode* headx = meet->next;
        //将meet结点的next指针置为NULL
                meet->next = NULL;        

                return getIntersectionNode(head,headx);//将找到的相交结点返回
            }
       }
       return NULL;  //如果fast为空,或者fast->next为空,说明链表没有成环,返回NULL
}

片尾

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

点赞收藏加关注 !   !   !

谢谢大家 !  !  !

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《AI创业浪潮:展望未来,解锁颠覆性创新机遇》

在科技革命的浪潮中,人工智能(AI)犹如一艘引领航向的旗舰,以其强大的变革力量重塑各行各业。随着技术的日益成熟与应用场景的不断拓宽,AI技术为创业者铺就了一条充满机遇与挑战的道路。本文将深入探讨未来AI技术领域的…

1.3 字符设备驱动

1、字符设备驱动工作原理 2、file_operations结构体 struct file_operations { struct module *owner; //拥有该结构的模块的指针,一般为THIS_MODULES loff_t (*llseek) (struct file *, lof…

10 Php学习:循环

在 PHP 中,提供了下列循环语句: while - 只要指定的条件成立,则循环执行代码块do…while - 首先执行一次代码块,然后在指定的条件成立时重复这个循环for - 循环执行代码块指定的次数foreach - 根据数组中每个元素来循环代码块 当…

(学习日记)2024.04.15:UCOSIII第四十三节:任务消息队列

写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈。 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录&a…

橱窗带货真的能赚到钱吗?橱窗带货素材哪里找?什么是橱窗带货?

什么是橱窗带货? 橱窗带货,一种以货架电商为基础的营销手段,可类比线下实体店的展示柜和窗口。橱窗的曝光量较高,因此通常展示店铺中的重点产品和推荐系列商品。短视频已经积极进军电商领域,虽然一开始主打直播电商&a…

newman下载安装

postman是专为接口测试而生,Newman是专为postman而生。newman可以让我们的postman的脚本通过非GUI(命令行)的方式。 1. 安装node.js 安装步骤 查看已安装版本 node -v 2. 安装 Newman 运行命令:npm install -g newman,即可完成安装操作。 或者 npm i…

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

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

两步解决 Flutter Your project requires a newer version of the Kotlin Gradle plugin

在开发Flutter项目的时候,遇到这个问题Flutter Your project requires a newer version of the Kotlin Gradle plugin 解决方案分两步: 1、在android/build.gradle里配置最新版本的kotlin 根据提示的kotlin官方网站搜到了Kotlin的最新版本是1.9.23,如下图所示: 同时在Ko…

人事|基于SpringBoot+vue的人事管理系统设计与实现(源码+数据库+文档)

人事管理系统目录 基于SpringBootvue的人事管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师,…

链表中常见的使用方法逻辑整理

文章目录 1. 链表特点2. 链表创建3. 链表遍历通用方法3.1 在链表的开头添加元素3.2 在链表的结尾添加元素3.3 删除链表的第一个元素3.4 删除链表的最后一个元素3.5 遍历链表3.6 查找链表中的元素3.7 反转链表 4. 常见面试题4.1 相交链表4.2 反转链表4.3 环形链表4.4 环形链表 I…

Nacos-默认token.secret.key-配置不当权限绕过漏洞复现

漏洞描述: Nacos 身份认证绕过漏洞(QVD-2023-6271),开源服务管理平台 Nacos在默认配置下未对 token.secret.key 进行修改,导致远程攻击者可以绕过密钥认证进入后台,造成系统受控等后果。 漏洞信息 公开时间:2023-03…

Windows安装MongoDB结合内网穿透轻松实现公网访问本地数据库

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Java 基于微信小程序的校园失物招领小程序,附源码

博主介绍:✌程序员徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

Java基础第十一课——类与对象(2)

由于类与对象这一部分的知识点很多,而且操作方法也有很多,所以这次将继续深入讨论一下关于类与对象中方法传参、方法重载、构造方法以及this关键字使用方面的知识。 一、方法传参 1.return关键字 return关键字作用 作用场景:方法内 作用…

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

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

NCF代码运行

keras 2.1.4 tensorflow 1.12.0 python 3.6.4 numpy 1.14.5 一、准备工作 1、安装虚拟环境 conda create -n tensorflow python3.6.4conda activate tensorflowconda install tensorflow1.12.0conda install keras2.1.4conda info2、安装相应依赖 cd Py3_Recommender-Syste…

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

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

对装饰器模式的理解

目录 一、场景二、面对场景中的新需求,我们怎么办?1、暴力法:直接修改原有的代码。2、子类继承法:既然要增强行为,那我搞一个子类,覆写不就完事了?3、装饰器模式 三、对装饰器模式的思考1、从代…

C语言-----结构体详解

前面已经向大家介绍过一点结构体的知识了,这次我们再来深度了解一下结构体。结构体是能够方便表示一个物体具有多种属性的一种结构。物体的属性可以转换为结构体中的变量。 1.结构体类型的声明 1.1 结构体的声明 struct tag {member-list;//结构体成员变量 }vari…

Shopee(虾皮)有哪些站点?

虾皮(Shopee)是东南亚地区的跨境平台,目前拥有多个站点,以下是对虾皮各站点的简要介绍: 中国台湾站:作为大多数新卖家的默认首站,台湾站没有语言障碍,运营起来更为方便,…