C语言之带环链表

带环链表是数据结构链表中的一个经典问题,这里我们研究该问题分为两个方向:链表是否带环、返回链表的入环节点。

下面我们通过两个题目来分析带环链表:

1.判断链表是否带环

141. 环形链表 - 力扣(LeetCode)

 那么我们要怎么判断一个链表是否带环呢?

这里我们直接给出答案:借助快慢指针来判断链表是否带环。

我们创建两个指针变量slow和fast,slow一次走一步,fast一次走两步,如果链表带环,那么fast就会先进入环,当slow也进入环之后,它们就开始了追击,因为fast走得比slow快,所以fast和slow一定会相遇,如果fast和slow相遇了,就证明了该链表是带环链表;如果fast走到了NULL,那么就说明该链表是不带环的。

分析图如下:

 这个逻辑还是很简单的,该操作的代码也很简单,这里直接给出:

bool hasCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }
    return false;    
}

到这里了就有问题了,那这样子写的原理是什么呢?我们借助图来解释一下:

到这里,问题就解决了么?其实还没有,fast走2步可以,那么fast走3步,4步,n步可不可以呢? 

下面我们来研究一下fast走3步的情况:

1.1fast走3步

我们借助图来分析:

我们看到,如果C-1是奇数的话,他就会陷入循环中,每一次都追不上,且每一次的距离都是C-1。那么fast走4步和走n步都是同样的分析道理,大家感兴趣的话可以自己推一下。 

我们现在就要问了,fast走三步真的追不上么?

1.2fast走三步到底追不追得上?

我们在上面说当N是奇数且C-1是奇数的时候就追不上,那么事实是这样的么?我们继续分析一下。我们假设进环前的路程为L,所以slow走的路程就是L,fast走的路程是L+x*C+N。x表示fast在环中走的圈数,因为在slow进环之前,fast可能已经在环中走了好几圈了。N表示slow刚入环时,fast和slow的距离。

然后又因为fast一次走三步,slow一次走一步,所以fast走的路程是slow路程的三倍。所以有等式:3L = L+x*C+N。化简得:2L = x*C+N。

综上所述,当fast一次走三步时,不管slow刚进入环时,fast和slow的距离N是奇数还是偶数,都会追上。偶数会在第一次追击就追上;奇数则会在第二次追击追上。 

2.返回带环链表的第一个入环节点 

142. 环形链表 II - 力扣(LeetCode)

我们在先前判断了一个链表是否带环,我们现在要返回该带环链表的入环节点。 

如上图所示,我们要返回的入环节点就是节点2。 

其实非常的简单,我们现在已经知道,不管fast一次走多少步,fast和slow总会相遇。我们定义连个指针head和meet,分别从链表的第一个节点和fast、slow相遇的节点开始遍历,等到head和meet相等的时候,此时两个指针指向的就是环入口的节点。

这是什么原理呢?

fast和slow的路程为什么是这样的呢?

我们想,slow可不可能在环中走过超过一圈呢?当然不可能了。因为fast走的是slow的2倍,如果slow走了一圈,那么fast就走了两圈,它们肯定就已经相遇了。所以slow的路程是L+N。

而因为fast走得比较快,所以当slow入环时,fast已经在环中转了好几圈了。那么我们想一下,fast可不可能一圈都没转完?不可能,因为如果一圈都没走完,那么fast走的路程还没有slow的多,因为slow路程的2倍才是fast的路程。

所以根据上面的公式,以及fast一次走二步,slow一次走一步,我们可以得出一个等式:

2(L+N)= L+x*C+N。化简得:L+N = x*C ,继续化简得:L = x*C-N,我们将该式带入图中:

 其上,就是我的证明过程。head和meet同时遍历链表,当相等的时候,它们指向的都是入环的第一个节点。下面给出代码:

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            while(head != fast)
            {
                head = head->next;
                fast = fast->next;
            }
            return head;
        }
    }
    return NULL;
}

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

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

相关文章

# Redis 入门到精通(四)-- linux 环境安装 redis

Redis 入门到精通(四)-- linux 环境安装 redis 一、linux 环境安装 redis – 基于 Linux 安装 redis 1、基于 Center 0S7 或者 unbunt-18.04 安装 Redis 1)下载安装包wget http://download.redis.io/releases/redis-?.?.?.tar.gz 如&…

Unity最新第三方开源插件《Stateful Component》管理中大型项目MonoBehaviour各种序列化字段 ,的高级解决方案

上文提到了UIState, ObjectRefactor等,还提到了远古的NGUI, KBEngine-UI等 这个算是比较新的解决方法吧,但是抽象出来,问题还是这些个问题 所以你就说做游戏是不是先要解决这些问题? 而不是高大上的UiImage,DoozyUI等 Mono管理引用基本用法 ① 添加Stateful Component …

每日复盘-20240715

20240715 六日涨幅最大: ------1--------300807--------- 天迈科技 五日涨幅最大: ------1--------300807--------- 天迈科技 四日涨幅最大: ------1--------300807--------- 天迈科技 三日涨幅最大: ------1--------300713--------- 英可瑞 二日涨幅最大: ------1--------3007…

前端Vue组件化实践:自定义加载组件的探索与应用

在前端开发领域,随着业务逻辑复杂度的提升和系统规模的不断扩大,传统的开发方式逐渐暴露出效率低下、维护困难等问题。为了解决这些挑战,组件化开发作为一种高效、灵活的开发模式,受到了越来越多开发者的青睐。本文将结合实践&…

代码随想录训练营第三十六天 1049最后一块石头的重量II 494目标和

第一题: 原题链接:1049. 最后一块石头的重量 II - 力扣(LeetCode) 思路: 首先确认这是一道01背包问题的题目,如何转换:剩下尽可能小的重量,如何剩下呢?跟分割等和子集很…

基于RAG大模型的变电站智慧运维-第十届Nvidia Sky Hackathon参赛作品

第十届Nvidia Sky Hackathon参赛作品 1. 项目说明 变电站是用于变电的设施,主要的作用是将电压转化,使电能在输电线路中能够长距离传输。在电力系统中,变电站起到了极为重要的作用,它可以完成电能的负荷分配、电压的稳定、容错保…

基坑安全:自动化监测系统的革新力量

在日新月异的基坑工程领域,基坑安全自动化监测系统犹如一位守护者,以其独特的优势,为工程的安全与质量保驾护航。该系统集先进的测量仪器、计算机技术与现代传感技术于一体,对基坑的围护结构及周边环境进行全方位、高精度的实时监…

【C++基础】初识C++(1)

目录 一、认识C 1.1 C 相关概念 1.2 C的发展 1.3 C的关键字 1.4 第一个程序 二、命名空间 2.1 namespace的定义 2.2 命名空间的使用 三、C输入和输出 四、缺省函数 五、函数重载 一、认识C 1.1 C 相关概念 1983年,Bjarne Stroustrup在C语⾔的基础上…

内网安全:权限维持的各种姿势

1.Linux权限维持 2.Windows权限维持 目录: 一.Linux权限维持: 1.webshell: 2.定时任务: 3.SUID后门: 4.SSH Key免密登录后门: 5.添加用户后门: 二.Windows权限维持 1.计划任务后门&…

NetSuite RPA技术实践

近期有同学提出一个需求。 “需要存取的報表是存貨分類帳(stock ledger),將查到的各個[Item|Location]作為一組key,分別將報表中的「期末庫存量」「期末平均成本」「期末庫存量價值」這三欄的值,在每個月月底的時候自動將這個報表的這三欄數…

rollup打包工具

rollup打包工具 在学习vite和vue3源码的时候,接触到了rollup,所以过来学习一下 什么是rollup rollup是一个模块化的打包工具,会将javascript文件进行合并。比起webpack,webpack在打包的时候会进行代码注入(保障兼容性)&#xf…

位图——哈希思想的应用

三、位图 0、位图概念 所谓位图,就是用每一个比特位来存放某种状态(0或1),是一种哈希思想的应用,适用于海量数据,整数,数据无重复的场景。通常是用来判断某个数据存不存在的。(注意…

GaussDB DWS 详解

文章目录 GaussDB DWS 详解一、简介二、DWS的分布式架构架构概述关键组件 三、分布式查询数据查询流程SQL执行的示例 批注:本文引鉴了Forlogen博主的一些内容,并加以补充,以供学习了解。 GaussDB DWS 详解 一、简介 DWS(Data Warehouse Ser…

数据库-三范式

第一范式 1 数据库所有字段都只有单一属性。 2 单一属性由基本数据类型构成。 3 数据库的表都是二维的行与列。 例如上面的例子就不满足第一范式,因为是可以继续拆分的,拆分为更多的属性。 第二范式 1 符合第一范式 2 表必须有个主建 3 其它字段可以…

《0基础》学习Python——第十一讲__时间函数

一、时间函数是Python中的内置函数和模块,用于处理日期和时间相关的操作。以下是常用的时间函数的种类和用法: 1、time.time():返回当前时间的时间戳。 时间戳(timestamp)是一种表示日期和时间的方式,它是一…

Linux--USB驱动开发(二)插入USB后的内核执行程序

一、USB总线驱动程序的作用 a)识别USB设备 1.1 分配地址 1.2 并告诉USB设备(set address) 1.3 发出命令获取描述符 b)查找并安装对应的设备驱动程序 c)提供USB读写函数 二、USB设备工作流程 由于内核自带了USB驱动,所以我们先插入一个U…

CSS-0_3 CSS和单位

文章目录 CSS的值和单位属性值长度单位CSS和绝对单位CSS和相对单位百分比em & rem视口 颜色单位 碎碎念 CSS的值和单位 我们知道,CSS是由属性和属性值所组成的表 随着CSS的发展,属性不说几千也有几百,我从来不支持去背诵所有的可能性。…

K8S系列-Kubernetes基本概念及Pod、Deployment、Service的使用

一、Kubernetes 的基本概念和术语 一、资源对象 ​ Kubernetes 的基本概念和术语大多是围绕资源对象 Resource Object 来说的,而资源对象在总体上可分为以下两类: 1、某种资源的对象 ​ 例如节点 Node) Pod 服务 (Service) 、存储卷 (Volume)。 2、…

Nginx入门到精通五(动静分离)

下面内容整理自bilibili-尚硅谷-Nginx青铜到王者视频教程 Nginx相关文章 Nginx入门到精通一(基本概念介绍)-CSDN博客 Nginx入门到精通二(安装配置)-CSDN博客 Nginx入门到精通三(Nginx实例1:反向代理&a…

从0-1搭建一个web项目(页面布局详解)详解

本章分析页面布局详解详解 ObJack-Admin一款基于 Vue3.3、TypeScript、Vite3、Pinia、Element-Plus 开源的后台管理框架。在一定程度上节省您的开发效率。另外本项目还封装了一些常用组件、hooks、指令、动态路由、按钮级别权限控制等功能。感兴趣的小伙伴可以访问源码点个赞 地…