一网打尽带环链表问题(手把着手带你理解,木有基础也能看懂!)

news2024/10/6 6:40:18

目录

0.前言

1.认识带环链表

2.带环链表OJ问题简述

3.判断是否为带环链表

4. 寻找入环节点(法一:数学推理的魅力)

5. 寻找入环节点(暴力拆解带环链表的魄力)

6.番外:判断是否为带环链表(fast和slow的速度选择)


0.前言

本文所有代码及图片资源都已传入gitee,可自取

3链表OJ题p3(带环问题) · onlookerzy123456qwq/data_structure_practice_primer - 码云 - 开源中国 (gitee.com)https://gitee.com/onlookerzy123456qwq/data_structure_practice_primer/tree/master/3%E9%93%BE%E8%A1%A8OJ%E9%A2%98p3(%E5%B8%A6%E7%8E%AF%E9%97%AE%E9%A2%98)

1.认识带环链表

链表带环问题是一个非常经典的链表OJ题,本文将会进行细致剖析,首先第一个问题:什么是带环的链表呢

我们通常说的单链表是一个节点一个节点线性连接的,最后一个节点指向NULL,代表这个单链表的结束带环链表偏偏不这样,其最后一个节点并不指向NULL,而是又回来指向该单链表中的某个节点

这样就会导致一个非常严重的问题!!!经典的单链表你用cur去遍历这个链表是可以用while(cur){cur = cur->next;}遍历到尾NULL结束的,但是你现在对带环链表进行遍历的话,那cur就永远也走不到NULL了,就相当于cur一直在这个环里一直转圈!!!陷入死循环

 这就是带环链表的特点

2.带环链表OJ问题简述

141. 环形链表 - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle/142. 环形链表 II - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle-ii/

第一道题是给我们一个链表Head,让我们判断这是一个带环链表还是一个不带环链表。

 第二个问题是,如果判断出这是一个带环链表,那么请你找出入环点在哪,如下图,开始入环的节点是2这个节点,我们就是要找出2这个入环节点

下面我们依次解决这两个问题。

  3.判断是否为带环链表

我们如果以cur走到空作为循环的条件,那带环链表就会陷入死循环,这是我们不愿意看到的,那如何才能判断出这是一个带环/不带环链表呢? 

带环链表,那肯定是带环的,现实中我们的操场上的400m环形跑道也是环形的,在环形跑道上总会发生一件事情:套圈!!!尤其是现在环形跑道有一个跑得特别快的人和一个跑得特别慢的人,假设他们在进行5000m比赛,如果他们一直跑跑得特别快的人一定会在某一时刻追上跑得慢的人,然后跑得慢的人的套圈就意味着某个时刻跑得的人追上,即跑得的人相遇。而如果是100m比赛,这种没有圈,即没有环的跑道,就不存在套圈,即跑得特别的人会始终在跑得的人前面直到到达终点,他们永远不会相遇

 现在我们具象化,现在有两个人fast和slowfast2m/s的速度匀速前进,slow1m/s的速度匀速前进,且现在slow在fast前X米,即fast需要追X米才能追上slow,即fast要比slow多跑X米才会相遇。如果fast和slow一直在环中跑,fast总有一个时刻可以追slowX米,和slow相遇

 按照这个思路,如果我们定义两个快慢指针fast和slow,他们同时在起点head出发fast速度一次走两步fast = fast->next->nextslow的速度一次走一步slow = slow->next。

如果链表不带环,就是一条线走到底,那fast/fast->next会首先走到尾空NULL,fast和slow永远不会相遇

而如果链表带环那fast一定会一马当先入环,然后fast开始在环里转圈圈, 等待slow入环,等slow入环时fast和slow环内追击战就开始了!

每fast和slow的走一次,fast和slow就缩小一个节点的距离,若从slow入环的那一刻开始,slow和fast差X节点的距离,slow和fast走X次之后,即fast走2X个节点slow走X个节点,此时fast和slow追上相遇,即fast==slow。这是带环所必然经历的事情。

bool hasCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        //若不带环,fast会一直走到最后一个节点/NULL
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
            //入环之后,fast每次缩小与slow一个节点距离,一定会和slow相遇
            if(fast == slow)
            {
                return true;
            }
        }
        //不带环
        return false;
    }

4. 寻找入环节点(法一:数学推理的魅力)

第一个问题的基础上,我们引入第二个问题:如果带环,那入环节点在哪呢?

这其实是个数学问题!!!我们先摆出结论:(第一个问题的基础)如果fast和slow从起点head出发,fast一次走两步,slow一次走一步,如果链表带环,那到最后fast和slow一定会在环中的某个节点相遇这个相遇的节点位置我们记录为meetnode。(第一个问题延伸)然后记录两个节点node1,node2,一个节点从起点head出发,另一个节点从meetnode出发,node1和node2分别都一次走一步node1和node2一定会相遇,且他们相遇的地方就是入环点!!!!!

ListNode *detectCycle(ListNode *head) {
        //判断是否有环
        ListNode* fast = head;
        ListNode* slow = head;
        //无环fast会走完这个链表
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
            //如果带环,fast和slow会在环中某个节点相遇
            if(fast==slow)
            {
                //记录相遇位置为meetnode
                ListNode* meetnode = slow;
                //node1从meetnode走,node2从head走
                ListNode* node1 = meetnode;
                ListNode* node2 = head;
                //最终node1和node2一定相遇,且相遇点是入环节点
                while(node1!=node2)
                {
                    node1 = node1->next;
                    node2 = node2->next;
                }
                return node1;
            }
        }
        //无环情况
        return nullptr;
    }

 为什么会这样呢?下面我们证明这个结论:

我们先把fast在环中追到slow的场景塑造出来:

这个带环链表,到达入环点前的要走的直线距离是L个节点环的长度(环的节点数)为C,slow一次走一步,fast一次走两步,同时从起点head出发。

然后一定是fast先入环,slow一次走一步,当slow入环,即slow到达入环点的时候,假设此时fast距离slow有X步即fast要追slowX步才相遇

fast的速度是一次两步,slow的速度是一次一步,每次走完fast与slow缩小一步的距离,那走了X次之后,即fast走了2Xslow走了X,2*X - X = X,fast追上slow,与之相遇。然后我们标记相遇点为meetnode

 根据结论:我们知道如果此时让node1从meetnode出发,node2从head出发,最后node1和node2一定会相遇,且相遇点就是入环点!!!下面我们根据上面的全过程进行推论:

1.slow和fast一直都在走,slow的速度始终是一步fast的速度始终是两步,fast速度始终是slow的两倍,所以直至fast和slow在meetnode相遇,fast走过的总距离一定是slow走过的总距离的两倍!

而我们知道slow走了L距离到达入环点此时fast和slow相距X步,在环中开始fast和slow的追逐战,然后在环中走X次,即fast走2X步,slow走X步后,fast追上slow,即fast和slow在相遇点meetnode相遇。这个全过程slow走了L+X步,fast的速度始终是slow的两倍,所以从这个角度分析fast走的总距离一定是2(L+X)

2.我们知道fast首先走过L,先于slow入环,然后fast在环中逛游,然后等到slow到达入环点的时候此时fast和slow相距X步,一圈的步数是X,所以fast所处的位置是距离入环点C-X处,所以在slow到达入环点之前,fast一定是在环内走了C-X步到预定位置的。

可是fast在环内只能走C-X步吗?

从slow达到入环点之前,在这个过程中fast不仅是走C-X达到预定位置,有可能fast在到达预定位置之后,fast又在环中走了好多圈,也有可能fast一圈也没走完,可以说slow到达入环点之前fast在环中走了多少几圈,走了多少圈都是不一定的。即在slow达到入环点之前,fast在环内走了有可能走了n*C + C-X(n>=0),这样slow到达入环点的时候,fast所处的位置还是距slowX步的。

所以在slow到达入环点的时候fast已经走L + n*C + C-X了,然后slow到达入环点开启追逐战:此时fast和slow相距X,然后fast追了slowX步,即fast走2X步,slow走X步后,两者相遇

我们总结上述过程fast走的总距离是 L + n*C + C-X + 2X = L + n*C + C + X(n>=0)

从上面这个角度我们得出fast和slow相遇之前,fast走得总距离是2(L+X),也是(L + n*C + 2X),2L+2X =  L + n*C + C + X,所以我们得到 L = n*C + C - X。(n>=0)

然后让node1从head走node2从meetnode出发,node2从head走到入环点需要走L步,node1从meetnode出发,在node2走L步到达入环点node1会走C-X + n*C,即node1也会走C-X步到达入环点,然后走n圈还是在入环点,此时我们可以看到node1和node2一定会在入环点相遇

所以结论正确:

(第一个问题的基础)如果fast和slow从起点head出发,fast一次走两步,slow一次走一步,如果链表带环,那到最后fast和slow一定会在环中的某个节点相遇这个相遇的节点位置我们记录为meetnode。(第一个问题延伸)然后记录两个节点node1,node2,一个节点从起点head出发,另一个节点从meetnode出发,node1和node2分别都一次走一步node1和node2一定会相遇,且他们相遇的地方就是入环点!!!!!

5. 寻找入环节点(暴力拆解带环链表的魄力)

第一个问题的基础上fast和slow一定会在环内相遇,记录相遇点为meetnode,然后我们对链表做拆解把meetnode节点与它的下一个节点newhead做分离,即newhead->next = node改为newhead->next = NULL。完成链表的拆解,带环链表变相交链表。

 然后这个环形链表就转化为下面这个问题:

 然后就变成了head和newhead两个相交链表,我们只要求得相交链表的第一个相交节点即为入环点的位置

对于相交链表的思路,可以在我的博客(一网打尽链表的经典OJ题!链表必考笔试题第二弹)中找到,这里我们简述一下思路,先求出分别求出相交链表1和2的长度len1与len2然后算出差距步abs(len1-len2),然后让较长的相交链表先走差距步走完差距步之后两个链表再同时出发直到他们相等就停下,此时停下的相等的节点的位置就是两个相交链表的起始相交节点,即入环点

 ListNode *detectCycle(ListNode *head) {
        //判断是否有环
        ListNode* fast = head;
        ListNode* slow = head;
        //无环fast会走完这个链表
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
            //如果带环,fast和slow会在环中某个节点相遇
            if(fast==slow)
            {
                //记录相遇位置为meetnode
                ListNode* meetnode = slow;
                ListNode* newhead = meetnode->next;
                //把meetnode节点的后一个节点newhead的next置空,拆解链表
                meetnode->next = nullptr;
                //然后就变成了head和meetnode两个相交链表
                //我们只要求得相交链表的第一个相交节点即为相遇点
                int len1 = 0,len2 = 0;
                ListNode* cur1 = head;
                ListNode* cur2 = newhead;
                while(cur1)
                {
                    ++len1;
                    cur1 = cur1->next;
                }
                while(cur2)
                {
                    ++len2;
                    cur2 = cur2->next;
                }
                int derta_len = abs(len1-len2);
                ListNode* longlist = head;
                ListNode* shortlist = newhead;
                if(len1<len2)
                {
                    longlist = newhead;
                    shortlist = head;
                }
                while(derta_len--)
                {
                    longlist = longlist->next;
                }
                while(longlist!=shortlist)
                {
                    longlist = longlist->next;
                    shortlist = shortlist->next;
                }
                return longlist;
            }
        }
        //无环情况
        return nullptr;
    }

6.番外:判断是否为带环链表(fast和slow的速度选择)

判断链表是否带环&&如果带环求出入环点位置,这两个问题我们已经得到实质的解决,可是秉承着一颗探索的心,最后我们回到第一个问题。

 如果fast的速度取每次两步,slow的速度取每次一步,我们先假设slow到入环点的时候,fast距离slow是X步,那么在这个场景下fast和slow是一定可以相遇,因为每次fast和slow的速度差是每次1步,也就是说,X可以整除1走X次之后就可以一定可以相遇

那如果fast的速度不是每次两步,而是每次三步,每次四步,甚至每次五步,在环内fast和slow还一定能相遇吗?会不会遇到这种情况:fast可以追上超过slow,却永远不能与slow相遇。当然是有可能的!我们随便举一个例子你就懂了:

 若环的长度C=2,当slow到达入环点的时候,fast和slow差1步,即起始fast和slow的差距为X=1。

 在这种情况下,fast和slow是永远不能相遇的,为什么这么说:fast和slow速度差是3-1=2,即每次两步,即每次走完fast能比slow多走2步。运用高中物理运动学的知识,我们可以采用参考系相对静止的方法,让slow静止fast以每次2步的在走,此时fast和slow相遇,与fast走每次3步,slow每次1步是等效的。这是第一个我们要使用的技巧。同时我们也可以看到fast能否和slow相遇,看的并不是fast和slow的绝对速度,而是fast和slow的相对速度差!

我们现在在参考系相对静止的场景下,让slow静止,fast以相对速度差前进:能否相遇看的就是能不能让fast走到slow的位置,那fast如何才能走到slow的位置呢,最简单的当然是fast正好走X步到达slow所在的位置,但是fast的相对速度差,即每次2步,不一定可以整除X,如果X是奇数,即如图X==1,即很有可能fast无法正好一步到位slow,而跑到slow的前一个位置。如上面一个例子,slow静止,fast每次两步,fast每次走完都会回到原位,永远不可能与slow相遇!!!而如果fast的速度取每次两步,slow的速度取每次一步,即相对速度差是1时,那相遇就是板上钉钉的事情了。那为什么相对速度差是非1(如上例是2)的时候,有可能遇到fast和slow永远无法相遇的情况呢?

我们仍然以相对速度差为2举例,做出如下具体分析:

 如果相对速度差为3,从这个例子我们推而广之,那fast和slow能否相遇的命题就是这样的:

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

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

相关文章

树上的DP

ACACAC来的如此之快&#xff0c;让我以为还要再调试几个点(h)&#xff0c;本文涉及思路参考&#xff1a;参考文章 嗯,这个题和思路让我对跨父节点的树上链有了一个认识&#xff0c;我们来看一下&#xff1a; 如果说&#xff0c;我们要对这样的一条链进行dpdpdp操作&#xff0c…

[GXYCTF2019]BabysqliV3.0

发现我们随便上传一个以后返回的是 alert(Wrong pass); 密码错误 如果上传一个账号永真的话返回 Not this user! 没有这个用户&#xff0c;所以感觉可以使用盲注&#xff0c;对账号进行盲注 可是题目 ascii(substr((select database()),1,1))>1 也不对 最后才知道是…

电脑组装配件知识

目录 1.电脑硬件基础知识 1.1CPU 1.2内存 ​编辑 1.3硬盘 1.4主板 1.5显卡 ​编辑 1.6显示器 1.7电源 1.8机箱 2.电脑硬件搭配及选购 2.1硬件搭配原则 2.2怎样查询软件或游戏配置 2.3配件购买注意事项 2.4搭配一台普通办公电脑 3.电脑组装 1.电脑硬件基础知识 …

软件测试/测试开发 | 一文学会 Appium 环境配置

Appium 是一个开源的、跨平台的测试框架&#xff0c;可以用来测试 Native App、混合应用、移动 Web 应用&#xff08;H5 应用&#xff09;等&#xff0c;也是当下互联网企业实现移动自动化测试的重要工具。Appium 坚持的测试理念&#xff1a; •无需用户对 App 进行任何修改或…

OAuth2.0 开放平台认证授权开发套件 Authmore-Framework 1.0 发布

简介 | Intro 基于 OAuth2.0 协议的开放平台认证授权开发套件, 包含授权服务和开放平台 Docker 镜像&#xff0c;基于 Spring Boot Starter 的资源服务工具包和客户端&#xff08;第三方应用&#xff09;工具包 优点 | Advantages 简洁&#xff1a;专注核心功能 —— 社会化…

剑指offer----C语言版----第十六天----面试题22:链表中的倒数第k个节点

目录 1. 链表中倒数第 k 个节点 1.1 题目描述 1.2 思路一 1.3 思路二&#xff1a; 1.4 总结----代码的鲁棒性 1. 链表中倒数第 k 个节点 原题链接&#xff1a; 剑指 Offer 22. 链表中倒数第k个节点 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/l…

竹制品行业市场运行态势及未来前景分析

2023-2029年中国竹制品行业市场运行态势及未来前景分析报告报告编号&#xff1a;1691644免费目录下载&#xff1a;http://www.cninfo360.com/yjbg/qthy/qt/20230110/1691644.html本报告著作权归博研咨询所有&#xff0c;未经书面许可&#xff0c;任何组织和个人不得以任何形式复…

基于Power BI的终端消费会员客户价值RFM分析

一、原理&#xff1a;RFM分析 &#xff08;一&#xff09;概念 RFM模型衡量当前客户价值和客户潜在价值的重要工具和手段。通过一个客户的近期购买行为、购买的总体频率以及花了多少钱三个维度来描述该客户价值状况的客户分类模型。 &#xff08;二&#xff09;分析维度 R&a…

使用标量衍射理论和菲涅耳和弗劳恩霍夫衍射的衍射对波动光学进行建模(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 本文使用Matlab对波动光学进行建模。包括使用标量衍射理论和菲涅耳和弗劳恩霍夫衍射的衍射。 &#x1f4da;2 运行结果 &#…

北大硕士LeetCode算法专题课-栈、队列相关问题

算法面试相关专题&#xff1a; 北大硕士LeetCode算法专题课--链表相关问题_ 北大硕士LeetCode算法专题课-查找相关问题_ 北大硕士LeetCode算法专题课-字符串相关问题_ 北大硕士LeetCode算法专题课-数组相关问题_ _ 北大硕士LeetCode算法专题课-基础算法之排序_ 北…

四、Javaweb之Filter、Listener、Ajax、Vue

文章目录12. Filter 和 Listener12.1 Filter快速入门和执行流程12.2 Filter使用细节12.3 Filter案例&#xff1a;登录验证12.4 Listener13. AJAX13.1 AJAX快速入门13.2 AJAX案例13.3 Axios异步框架13.4 JSON13.5 JSON案例SelectAllServlet.javabrand.htmlAddServlet.javaaddBra…

某集团汽车配件电子图册性能分析案例(二)

背景 汽车配件电子图册系统是某汽车集团的重要业务系统。业务部门反映&#xff0c;汽车配件电子图册调用图纸时&#xff0c;出现访问慢现象。 某汽车集团总部已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和历史原始流量。本次分析重点针对汽车配件电子图…

JDBC简介及原理和使用介绍

JDBC简介及原理和使用介绍JDBC简介 jdbc概述 ​ Java数据库连接&#xff0c;&#xff08;Java Database Connectivity&#xff0c;简称JDBC&#xff09;是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口&#xff0c;提供了诸如查询和更新数据库中数据的方法。 …

WebGL-iTwin.js】实战篇(二):用nodejs代码详解iTwin.js中PhysicalObject生成方法

PhysicalObject 即真实存在的物理对象&#xff0c;比如&#xff1a;电脑、桌子等。在webgl中&#xff0c;我们人眼能看到的模型都是PhysicalObject&#xff0c;由多种几何图元类型构成&#xff0c;如&#xff1a;网格、实体、曲面、曲线&#xff0c;点云等。 其中带索引的多边…

XCTF:cat

打开是一个输入框&#xff0c;要求输入域名&#xff0c;尝试输入baidu.com进行测试 并无任何回显&#xff0c;测试下127.0.0.1本地地址 执行成功&#xff0c;并出现ping命令结果&#xff0c;这里很容易联想到命令注入&#xff0c;尝试进行命令拼接注入 但测试了常用的拼接字…

【5】变量和常量

一、什么是变量 首先我们要理解这么一个概念&#xff0c;在程序的运行过程中所有数据是保存在内存中的&#xff0c;我们代码中想使用这个数据的时候就要从内存中找&#xff0c;而变量的作用类似就是将内存地址保存&#xff0c;之后直接通过这个变量找内存中的数在Go语言中&…

JNI和Ndk开发

按照一下配置&#xff0c;基本能保证demo跑通 1、下载NDK&#xff0c;下载需要的版本 2、下载Cmake版本 3、项目结构&#xff1a;含C源码 4、编写JNI的加载类 public class YjkModel {static {System.loadLibrary("nativ"); //跟CMakeLists.txt 库名一致}public nat…

基于 APISIX 的服务网格方案 Amesh 积极开发中!

作者lingsamuel&#xff0c;API7.ai 云原生技术专家&#xff0c;Apache APISIX Committer。 在云原生快速发展的前提下&#xff0c;服务网格领域也开始逐渐火热。目前阶段&#xff0c;大家所熟知的服务网格解决方案很多&#xff0c;每种产品又各有其优势。因此在面对不同的行业…

python直接赋值、浅拷贝与深拷贝

本文主要参考这篇博文python浅拷贝与深拷贝 基本概念 首先了解python中的一些基本概念 变量&#xff1a;是一个系统表的元素&#xff0c;拥有指向对象的连接空间对象&#xff1a;被分配的一块内存&#xff0c;存储其所代表的值引用&#xff1a;是自动形成的从变量到对象的指…

ECU Extract + OS Task Mapping 步骤

纲要&#xff1a; 通过 Composition里面的Assembly Connection (Intra-ECU Communication)System Extract 里面的SystemDataMapping (Inter-ECU Communication) 已经把SWC的所有Data Element都连接上了&#xff0c;接下来就是把SWC的Runnable给Mapping到对应的OS Task上&…