数据结构算法——链表带环问题——数学深度解析

news2024/11/17 9:32:10

        前言:本节内容主要是讲解链表的两个问题 :1、判断链表是否带环; 2、一个链表有环, 找到环的入口点。 本节内容适合正在学习链表或者链表基础薄弱的友友们哦。

        我们先将问题抛出来,友友们可以自己去力扣或者牛客网去找相应题目, 这里直接贴链接:(没有做过这两个题的友友 千万! 千万! 千万! 要先自己做一下这两个题。)

        判断链表是否带环:141. 环形链表 - 力扣(LeetCode)

        带环链表的入环节点:LCR 022. 环形链表 II - 力扣(LeetCode)

        

目录

判断链表是否带环

题目解析

算法原理

算法演示

算法原理

原理扩展

环形链表的入口节点

题目解析

算法原理

算法演示

算法原理


        我们先来讲解第一道题

判断链表是否带环

题目解析

题目:

        

代码框:

        题目非常的简单, 就是要求我们设计一个算法, 判断这个链表中是否右带环结构就可以了。 如果有带环结构, 那么就返回true, 如果没有带环结构, 那么就返回false。 

算法原理

算法演示

        解决这个问题需要用到快慢双指针算法, 我们利用题中所给示例进行演示:

        先定义两个指针slow, fast。并且slow和fast要指向同时指向头节点, 否则在第二道题的时候处理起来会变的复杂

        然后, 我们向后进行遍历, 遍历的过程是这样的: slow指针一次向后移动一个节点。 fast一次向后移动两个节点。当两个节点相遇的时候就说明我们的链表是带环的。

        而如果我们的fast指向了空节点, 那么就说明我们的链表是不带环的。

        我们演示一遍是这样的:

代码贴图如下:

bool hasCycle(struct ListNode *head) 
{
    //先判断下链表为空的情况
    if (head == NULL) return false;

    //1、创建两个指针, slow, fast同时指向链表头节点。
    struct ListNode* slow = head;
    struct ListNode* fast = head;

    //2、遍历整个链表, 判断是否有环
    while (fast != NULL && fast->next != NULL)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) return true;  //如果两个指针指向同一个位置, 说明有环。
    }
    return false;             //退出循环说明无环。
}

        这就是算法的基本思路, 我们接下来进行剖析这个算法:

算法原理

        

        为了证明我们的结论的普遍性, 我们利用上面的抽象图来进行演示。

         这里的环周长就相当于C个节点, 前面的直线L就相当于没有进入环之前的L个节点。 然后slow每次走一个节点, fast一次走两个节点。 

        fast走的比slow要快, 所以, fast一定是在slow前面的。如果没有环的话, fast是肯定会先一步指向空的。 fast和slow也就无法相遇了。 所以如果fast指向空, 那么就一定没有环。

         但是如果有环, 这个时候fast一定会先进入环。如图所示:

        然后继续遍历, slow再进入环。如图所示:

        那么, 重点就来了。 slow进入环之后, 如果fast和slow再继续遍历, 那么是不是fast和slow之间的距离在不断的缩小, 是不是就相当于fast在追击slow。 我们假设fast到slow的距离为N, 此时我们就将问题转化为了一个追击相遇问题。  

        fast每次走两步, slow每次走一步。 那么每回合fast和slow之间的距离就减少1。 循环下来就是N - 1  - 1 - 1 - 1 - 1, 直到N为零位置。 此时fast和slow相遇。并且当这两个指针相遇的时候, 只有两种情况, 一个是在环的入口点相遇, 一个是在环的其他位置相遇。 但这两种情况可以归到一类里面——slow和fast会在环内相遇

        

 综上, slow和fast只要相遇了, 他们就会在环内, 说明链表有环。

        然后到这里这个题的算法原理基本结束了, 但是, 这里还有一个经常考的探究性问题, 很重要的扩展知识。 接下来进行分析:

原理扩展

        从上文我们知道, 当slow指针和fast指针相遇的时候一定再环内。 但是有没有可能slow和fast不会相遇, 每次fast指针都越过slow指针, 导致两个指针永远无法相遇?

        答案是我们前面分析的fast走两步, slow的情况不会。

        但是如果fast一次走三步, slow一次走一步以及一些其他情况就可能fast直接越过slow, 永远不会相遇。这里需要具体情况具体分析。

        分析过程如下:

        如果我们slow每回合走一步, fast每回合走三步。 那么fast和slow的步数就相差2。 假设slow进环的时候slow和fast之间的距离相差N。那么循环下来就是N - 2 - 2 - 2. 这里如果N是偶数, N就恰好减少到零了, 这个时候slow和fast指针就相遇了。

        但是如果N是奇数呢? N - 2 - 2 - 2就有可能得到-1,我们假设圆环的长度为C,这个时候就是如图这种情况:

        那么, fast和slow之间的距离此时变成了C - 1

        接下来再进行分类讨论, 如果C为奇数, 那么C - 1就为偶数, 这样 C - 1 - 2 - 2 - 2……就有可能减少到零; 如果C为偶数, 那么C - 1就是奇数, 这样 C - 1 - 2 - 2 ……就会重新减少到-1, 然后fast和slow之间的距离又变成C - 1, 这个时候就陷入了死循环。

         所以,综上我们可以得出小结论: 当fast一次走三步, slow一次走一步。如果N为偶数, 那么fast和slow一定相遇。 如果N为奇数, C为奇数。 那么fast和slow也会相遇。 如果N为奇数, C为偶数, 那么fast和slow永远不会相遇。

        将上面的推导过程总结为一个算数表达式就是 : (N + x * C)% 2  ? 0//距离N + x圈后模上fast和slow步数差是不是等于0.

        所以, 我们得出的大结论就是:假设slow和fast每回合的步数相差sub.进入环的时候fast指针与slow指针之间的距离为N。圆环的长度为C。如果有 :  (N + x * C)% sub  ==  0。就说明fast和slow会相遇, 否则不会相遇。

        以上就是本道题的所有知识点。

        ps:代码中的细节问题,属于代码编写的范畴, 不属于算法原理。 本篇内容只讲算法原理。 代码友友们自行编写调试。



环形链表的入口节点

题目解析

题目:

代码框: 

        

 这道题就是上面那道题的提高版本。 需要先对链表判断是否有环, 然后再判断入环节点。

算法原理

要找到环的入口点同样是有结论的, 这里我们先用结论进行代码的展示。 再进行分析

算法演示

        寻找环的入口点的算法就是先利用上面一题的方法先判断是否存在环。 这个时候如果存在环的话fast指针和slow指针会在环内的某一个点相遇。

        然后,我们定义一个meet指针指向这个相遇节点。 然后再重新定义一个指针指向链表的头节点。 让meet指针和指向头节点的指针同时向后遍历。 最后相遇的节点就是我们环的入口节点。 (原理是数学证明, 后面会进行证明。)

        如图为代码贴图:

struct ListNode *detectCycle(struct ListNode *head) 
{
    //1、定义快慢指针
    struct ListNode* slow = head;
    struct ListNode* fast = head;

    //2、循环判断是否有环
    while (fast != NULL && fast->next != NULL)
    {
        //fast走两步, slow走一步。
        slow = slow->next;
        fast = fast->next->next;

        //如果相遇, 说明有环。 然后寻找入环节点
        if (slow == fast)
        {
            //meet节点指向fast和slow相遇节点。
            struct ListNode* meet = slow;
            //让slow重新指向头节点
            slow = head;
            //如果两个指针不相等, 就让他们向后遍历。
            while (slow != meet)
            {
                slow = slow->next;
                meet = meet->next;
            }
            //最后返回相遇节点
            return meet;
        }
    }
    return NULL;  //从循环中出来说明fast走向了空, 说明没有环。
}

算法原理

        要证明为什么从相遇位置和头节点的两个指针同时向后遍历, 相遇节点就是入环节点。我先给一张抽象图, 方便观察与理解:

假设我们从图中时刻开始向后遍历。 首先, fast先进环。如下图:

        然后, fast继续向后走。 一直到slow进环:

        好, 在这里停住, 这里有很重要的问题。 就是这个fast此时在环中已经走了几圈? 

        这个圈数确定吗?答案是不确定。 因为我们并不知道这个圈的大小, 如果这个圈很小很小。 然后前面的直链很长, 那么从fast进环到slow进环这一段时间中fast就可能在环中转了很多很多圈。

        我在这画出一个例子就好懂了:

        从这个例子我们可以看出, 在fast到slow这段时间内, fast在环中走的圈数是不确定的。

        知道了这点后, 我们继续向下遍历, 一直到slow和fast相遇。

        现在, 另外一个重要的问题就是:从slow进环到被fast追上, slow转了几圈?

        我们看这样一个例子:

        如果当slow进环的时候, fast恰好在slow前面一个位置。 如图:

        那么到fast追上slow的时候, slow转的了一圈吗?

        我们利用方程算一下:假设slow行走的路程是s, 那么fast就是2s。 此时就有:2s - s = C - 1;

得到的就是s == C - 1; 很显然就算在这种极端情况下slow都没有走一圈, 那么其他情况下更不可能走一圈。 那么上面的问题的答案就是: 从slow进环到被fast追上, slow一圈也没有转。

        有了这些的铺垫后。 我们再从整体出发看 : 从两个指针开始遍历到两个指针相遇。 设slow行走的距离是X。 那么fast指针行走的距离就是2 * X;  我们设从入环到节点到slow和fast相遇的位置的距离为N。如图:

        那么就有N + L = X;所以slow行走的距离就是N + L;fast行走的距离就是2 *(N + L)

        但是, 对于fast来说, 还有另外一个式子: fast在从入环到slow入环期间,我们分析过了。 fast可能行走了几圈。 我们设为k圈。  然后从入环节点到相遇位置为N。 那么就有了fast的行走距离又可以有 : L + k * C + N;

        那么就有  2 *(N + L)== L + k * C + N

        计算后就是k* C - N == L;  也可以写成 : (k - 1) * C + (C - N) == L

        而我们的相遇节点到入环节点的距离恰好是 C - N; 所以,根据这个式子。 我们就可以证明如果从相遇节点和头节点同时向后遍历, 那么最后再次相遇时的节点就是入环节点。 

---------------------------------------------------------------------------------------------------------------------------------

        以上, 就是本节的全部内容。

       

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

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

相关文章

STM32 F103C8T6学习笔记17:类IIC通信—MLX90614红外非接触温度计

今日学习配置MLX90614红外非接触温度计 与 STM32 F103C8T6 单片机的通信 文章提供测试代码讲解、完整工程下载、测试效果图 本文需要用到的大概基础知识:1.3寸OLED配置通信显示、IIC通信、 定时器配置使用 这里就只贴出我的 OLED驱动方面的网址链接了&#xff1a…

线上线下收银一体化,新零售POS系统引领连锁门店数字化转型-亿发

在市场竞争日益激烈的背景下,没有哪个商家能够永远屹立不倒。随着互联网技术的快速发展,传统的线下门店面临着来自电商和新零售的新型挑战。实体零售和传统电商都需要进行变革,都需要实现线上线下的融合。 传统零售在客户消费之后就与商家失…

群晖Docker部署Java jar包

安装群晖Docker组件Container Manager 打开组件,找到注册表搜索java,安装java映像 安装完成后会在映像目录里展示出来,点击运行 常规设置按需配置即可 这里要设置一个文件夹目录给这个容器来使用 一般来说给个/root来用就够了 设置完成后可以在容器目录里看到容器已经运行起来了…

判断循环链表以及其环入口

文章目录 题目题目链接题目要求 解题思路方法一:哈希表方法二:双指针 进阶思考快指针一次走三步 进阶问题(入口点)题目链接题目要求问题思路 总结 题目 题目链接 环形链表 题目要求 解题思路 显而易见的是,单纯的遍…

可靠的Mac照片恢复解决方案

当您在搜索引擎搜索中输入“Mac照片恢复”时,您将获得数以万计的结果。有很多Mac照片恢复解决方案声称他们可以在Mac OS下恢复丢失的照片。但是,并非互联网上的所有Mac照片恢复解决方案都可以解决您的照片丢失问题。而且您不应该花太多时间寻找可靠的Mac…

uniapp 对接 Apple 登录

由于苹果要求App使用第三方登录必须要求接入Apple登录 不然审核不过 所以: 一、勾选苹果登录 二、 设置AppId Sign In Apple 设置完成重新生成描述文件 !!!!证书没关系 示例代码: async appleLogin…

2024年最新省市县三级行政区划数据(审图号:GS(2024)0650号)

2024年省、市、县三级行政区划数据由国家基础地理信息中心发布,通过《2024版国家地理信息公共服务平台(天地图)》正式对外提供。这份数据涵盖了最新的省市县三级行政区划信息,更新于2024年1月,提供了详细的矢量数据下载…

食谱管理和餐饮计划应用Mealie

放假除了休闲娱乐,也不能忘了美食 什么是 Mealie ? Mealie 是一个自托管的食谱管理和餐饮计划应用,具有 RestAPI 后端和基于 Vue 构建的响应式前端应用,为整个家庭提供愉快的用户体验。通过提供 URL,您可以轻松将食谱…

知识产权 | 守护科技创新之光,共筑知识产权长城

2024年4月26日,迎来了一年一度的世界知识产权日,今年的主题是:“立足创新创造,构建共同未来。” 易我科技是一家专注于数据安全产品研发、生产、销售、服务一体化的高新技术软件企业。易我科技自成立以来,始终秉持尊重…

贝叶斯回归

1. 贝叶斯推断的定义 简单来说,贝叶斯推断 (Bayesian inference) 就是结合“经验 (先验)”和“实践 (样本)”,得出“结论 (后 验)”。 2. 什么是先验? 贝叶斯推断把模型参数看作随机变量。在得到样本之前,根据主观经验和既有知…

微视网媒:引领新媒体时代的视觉先锋

在信息爆炸的时代,内容消费的方式日新月异,而“微视网媒”正是这场媒体变革中的佼佼者。凭借其独特的视角、精湛的制作和广泛的传播渠道,微视网媒不仅改变了人们获取信息的方式,更在不断地塑造着未来的媒体生态。 一、创新内容&am…

STM32中断之TIM定时器详解

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. TIM简述 2. 定时器类型 2.1 基本定时器 2.2 通用定时器 2.3 高级定时器 3. 定时中断 4. 代码示例1 5. 代码示例2 1. TIM简述 定时器的基本功能:定时器可以在预定的时间间隔内产生周…

LeetCode 198—— 打家劫舍

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 此题使用动态规划求解,假设 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 代表不偷窃第 i i i 个房屋可以获得的最高金额,而 d p [ i ] [ 1 ] dp[i][1] dp[i][1] 代表偷窃第 i i i 个房屋可以获…

2024年谷歌Google广告开户是什么政策?

2024年谷歌Google广告依然是众多企业拓展市场、提升品牌影响力的重要渠道,随着谷歌政策的不断更新与优化,以及对第三方cookie逐步禁用的决定,如何高效地在这一平台上开展广告活动,成为每一家企业必须面对的战略挑战。云衔科技作为…

【平台开发】MTK6833 实现lk下CQE接口移植 - cmdq irq

1.cmdq_irq 检测中断bit 2.目前遇到问题 任务执行后,没有触发对应中断,伴有错误发生,但任务完成标志位能检测到 寄存器CQIS,CQDQS等均为0,CQTCN为任务完成寄存器看到置1,CQTERRI检测到8000错误 错误详情如下&#xf…

基于SpringBoot+Vue外卖系统设计和实现(源码+LW+部署讲解)

🌹作者简介:✌全网粉丝10W,csdn特邀作者、博客专家、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 🌹 主要…

day7 c++

整理代码 1、unique_ptr 指针 #include <iostream> #include <memory> using namespace std; class Demo {public:Demo(){cout<<"无参构造"<<endl;}~Demo(){cout<<"Demo的析构函数"<<endl;} };int main() {//unique…

(1)探索 SpringAI - 基本概述

人工智能简介 A system is ability to correctly interpret external data, to learn from such data, and to use those learnings to achieve specific goals and tasks through flexible adaptation. 翻译&#xff1a;系统正确解释外部数据的能力&#xff0c;从这些数据中学…

【C语言/数据结构】经典链表OJ习题~第二期——链中寻环

&#x1f388;&#x1f388;&#x1f388;欢迎采访小残风的博客主页&#xff1a;残风也想永存-CSDN博客&#x1f388;&#x1f388;&#x1f388; &#x1f388;&#x1f388;&#x1f388;本人码云 链接&#xff1a;残风也想永存 (FSRMWK) - Gitee.com&#x1f388;&#x1f…

AI家居设备的未来:智能家庭的下一个大步

&#x1f512;目录 ☂️智能家居设备的发展和AI技术的作用 ❤️AI技术实现智能家居设备的自动化控制和智能化交互的依赖 AI家居设备的未来应用场景 &#x1f4a3;智能家庭在未来的发展和应用前景 &#x1f4a5;智能家居设备的发展和AI技术的作用 智能家居设备的发展和AI技术的…