【数据结构】带环链表(详解+拓展)

news2024/12/23 10:44:36

一.题目

在链表相关题目中,有一道非常经典的题目:带环链表(链接:141. 环形链表 - 力扣(LeetCode))。带环链表尾节点的next指针指向其他节点,因此遍历一个带环链表将是一个死循环,这是带环链表的基本特征,如示例所示。

 

二.解题

此题使用快慢指针能轻松解决。定义快指针fast和慢指针slow,快指针一次前进两步,慢指针一次前进一步,如此快指针一直领先于慢指针,率先进入环内,而后慢指针才进入环中,而后如果快指针追上了慢指针,那么就是带环链表。代码实现如下:可以看见是通过测试的

bool hasCycle(struct ListNode *head) {
    //定义快慢指针
    struct ListNode* fast = head,*slow = head;

    //若不是带环则遇到尾节点或 NULL停止
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }
    return false;
}

 

三.证明

然而这种写法真的合理吗,为什么快指针一定能追上慢指针,难道不存在一直错开的情况吗?

其实就上述代码而言,证明是很容易的。fast快指针一定快于slow慢指针进入环中,因此当slow刚好进入环中时,fast要么跟slow重合(特殊情况,此时直接就能判断带环了),要么有一定距离,令这距离为N,由于fast一次走两步,slow一次走一步,因此在环中每次移动fast距离slow的距离就会减1,N通过不断的减1终会减到0,此时fast和slow就重合了,因此这是一定能追上的。

四.拓展

上述证明只适用于fast走两步的情况,那么fast走三步,四步,五步,N步呢?我们改为fast一次走三步试一试,发现过关,改为四步试一试,仍然过关。

证明过程与上文相同吗?不一样,更为复杂,但证明过程是类似的,这里以走三步为例进行一次分析。

无论fast一次走几步,必然比slow快,更早进入环中,因此当slow进入环中时,fast必然和slow有一个距离N(N为0则直接判断为带环),fast每次走三步,速度差则为两步,因此N每次减2,能否减到0呢?若N为偶数,必然能减到0(此时fast和slow重合,则判断带环),若为奇数,那么最后会减到-1,即fast没有与slow重合,而是又超过了一步,那么此时fast要追上slow的距离则变为了C-1(C位环的节点数),此时又回到了距离的奇偶判断,若C-1为偶,则最后能为0,若C-1为奇,那么最后又会超过一步,永远无法重合。

那么总结一下,fast和slow一直无法重合的条件是:N为奇数并且C-1为奇数。即N为奇数并且C位偶数。 那么,这可能存在吗?使用简单的数学推导能得到:2L = X+1* C – N

能满足N为奇数并且C位偶数吗?这是不可能的,因为2L必然为偶数,而(X+1)*C也必然为偶数(一个数乘以偶数必然为偶数),N为奇数,偶数减奇数必然为奇数,这与2L为偶数相矛盾,因此不存在该情况,也就是说必然能追上。

五.相关题目

再来看一道与环形链表相关的题目:找到入环点(141. 环形链表 - 力扣(LeetCode))

 

 进行简单的数学推理得到:L = X*C-1+ C - N,其中X*(C-1)就是绕圈而已,在图中清晰可见,这样我们就有了思路:让head从头开始遍历L,此时另一个指针从相遇点开始遍历,等到他们相遇时,此时就是第一个入环点。代码如下:

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

 

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

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

相关文章

系统架构师复习材料总结---计算机软考

系统架构师复习材料总结 1、嵌入式指令集 2、逆向工程 3、自顶向下 4、数据库开发 5、软件架构 1 1 1 1 1 1 1 6、分布式系统 7、架构评价 8、构建复用 9、特定领域软件架构 10、面向对象分析模型 11、设计模式 12、项目变更 13、中间件开发 14、网络安全 15、电子商务…

近几年视频取证、视频篡改检测技术发展现状及挑战

前言 本文主要搜集了视频取证各个子领域近几年的高影响因子/引用数的文章及其主要思想和做法,旨在分析目前视频篡改检测的发展现状与热点领域,文章中也融合了自己的一点看法和展望,欢迎感兴趣的同学和我多多沟通。 本文无论是文献搜集还是方…

黑马面试篇1(续)

黑马面试篇1-CSDN博客(续集) 六、消息中间件篇 6.1 RabbitMQ 1)使用场景: 异步发送(验证码、短信、邮件…)MYSQL和Redis , ES之间的数据同步分布式事务削峰填谷… 2)RabbitMQ消息的重复消费问…

毅四捕Go设计模式笔记——命令模式

命令模式(Command Pattern) 为了解决什么问题? 命令模式的目的是将请求发起者和请求执行者解耦,使得请求的发起者不需要知道具体的执行者是谁,也不需要知道执行的具体过程,只需要发送请求即可。 通过使用…

使用 LooperPrinter 监控 Android 应用的卡顿

在 Android 开发中,主线程(UI线程)的卡顿直接影响用户体验。LooperPrinter 是一种有效的工具,可以帮助我们监测和识别这些卡顿。下面是如何实现 LooperPrinter 监控的详细步骤和相应的 Kotlin 代码示例。 步骤 1: 创建自定义的 P…

人脸识别开源算法库和开源数据库

目录 1. 人脸识别开源算法库 1.1 OpenCV人脸识别模块 1.2 Dlib人脸识别模块 1.3 SeetaFace6 1.4 DeepFace 1.5 InsightFace 2. 人脸识别开源数据库 2.1 CelebA 2.2 LFW 2.3 MegaFace 2.4 Glint360K 2.5 WebFace260M 人脸识别 (Face Recognition) 是一种基于人的面部…

2024年十五届蓝桥杯省赛大学B组真题(Java完整版)

2024年十五届蓝桥杯省赛大学B组真题(Java) 前言: 赛后一直犹豫要不要对比赛进行复盘出个题解,拖到了现在,终于也是等到比赛结果出来,看到没有辜负个人期望成功取得省一,决定在国赛前对省赛进行…

记录k8s以docker方式安装Kuboard v3 过程

原本是想通过在k8s集群中安装kuboad v3的方式安装kuboard,无奈在安装过程中遇到了太多的问题,最后选择了直接采用docker安装的方式,后续有时间会补上直接采用k8s安装kuboard v3的教程。 1.kuboard安装文档地址: 安装 Kuboard v3 …

外观模式【结构型模式C++】

1.概述 外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。 2.结构   外观角色(Facade):为多个子系统对外提供一个共同的接口,知道哪些子系统负责处理请求,将客户端的请…

Python使用设计模式中的建筑模式将数据写入Excel且满足条件内容标红

对于这个任务,适合使用"Builder"设计模式。Builder模式的主要目的是将对象的构建与其表示分离,以便相同的构建过程可以创建不同的表示。在这个情况下,我们需要一个构建器来逐行构建Excel表格,并根据给定的数据添加相应的…

MySQL--对于库的操作对于表的操作

一、库的查看创建删除 库的查看 show databases; 库的创建(创建一个test1数据库) create database test1; 同样,我们另起一个root会话,并执行 cd /var/lib/mysql 然后发现多了一个test1目录 删除库(删除test1数…

Java 为什么设计成 “String” 不能用 “==” 比较值?

Java中的String是一种特殊的对象类型,用于表示字符串。在Java中,String对象的创建和比较是一个重要的话题,其中,操作符在比较String对象时有着特殊的行为。为了了解Java为什么设计成String不能用比较值,需要深入探讨Ja…

VMamba原理

为了解决模型中的方向敏感性问题,我们引入了交叉扫描模块(CSM)。该模块能够遍历图像空间域,将任意视觉图像转化成有序序列。 VMamba降低注意力机制复杂度的概念来源于“具有选择性的扫描状态空间序列模型”(Selective…

力扣33. 搜索旋转排序数组

Problem: 33. 搜索旋转排序数组 文章目录 题目描述思路复杂度Code 题目描述 思路 1.初始化左右指针:首先,定义两个指针left和right,分别指向数组的开始和结束位置。 2.计算中间值:在left和right之间找到中间位置mid。 3.比较中间值…

人工智能分割分类model:nnUnet-paddle

文章目录 神经网络nnUnet和paddle都需要在Ubuntu下进行安装PaddleProject 神经网络 开源来自https://github.com/MIC-DKFZ/nnUNet 自建了仓库,但还不会用 来自 mmsegmentation有空去了解 . MICCAI 2020 也是用到这个网络 paddle上的是不是不能用… nnUnet和pad…

废旧锂电池污水如何处理

废旧锂电池中含有多种潜在有害物质,因此正确处理废旧锂电池产生的污水对环境保护至关重要。以下是处理这种污水的几个关键步骤: 收集与预处理: 废旧锂电池应首先在干燥、通风良好的环境中安全收集,避免污水泄漏。在开始处理之前&…

Ansible-Tower安装破解

主机IP地址版本Ansible192.168.169.2042.9.1Tower192.168.169.2043.6.2 基础环境 systemctl disable firewalld --now && setenforce 0 sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/config mv /etc/yum.repos.d/CentOS-* /tmp/ curl -o /etc/yum.repo…

【UE5】动态播放媒体

最近项目中有一个需求,需要将场景中的42块屏幕都显示媒体内容,想着如果每一块屏幕都创建一个MediaPlayer资产、一个MediaSource资产、一个MediaTexture资产及创建对应的Material,就是4*42168个资产需要维护了,所以想着就全部采用动…

数据库|TiDB-Server API的高效应用指南

一、API介绍 1.Status 显示TiDB 连接数、版本和git_hash 信息 tidb-server_ip:status_port/status { "connections": 0, "version": "5.7.25-TiDB-v6.1.1", "git_hash": "5263a0abda61f102122735049fd0dfadc7b7f822" } 2.St…

C语言:文件操作(中)

片头 嗨!小伙伴们,大家好!在上一篇中,我们学习了C语言:文件操作(上),在这一篇中,我们将继续学习文件操作,准备好了吗?Ready Go ! ! ! 文件的顺序…