环形链表的经典问题

news2024/11/16 9:46:56

环形链表

  • 环形链表的介绍
  • 链表中是否带环
  • 返回链表开始入环的第一个节点

本文主要介绍如何判断一个链表是否是环形链表,以及如何得到环形链表中的第一个节点。

环形链表的介绍

环形链表是一种链表数据结构,环形链表是某个节点的next指针指向前面的节点或指向自己这个节点的一个链表,这个链表就构成了环形链表。

链表中是否带环

要判断一个链表中是否带环,首先直接给出结论,我们可以用一个快指针(一次走两步),应该慢指针(一次走一步),如果该链表带环,最后快指针和慢指针就会在环中相遇,否则就是快指针走到空,这就表明该链表不带环。
判断一个链表是否带环Leetcode

根据这个思想,可以写出以下代码(快指针走两步,慢指针走一步):

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

分析:当链表为带环链表时,为什么快指针走两步,慢指针走一步就一定会在环中相遇?

当为环形链表时,快慢指针最总都会进入到环内。
在这里插入图片描述假设这个环是顺时针方向走的,当slow进入到带环的第一个节点,此时slow距离fast的节点个数为N,这个环的大小为C,示意图如上所示。fast走2步,slow走一步,那么两者之间的距离就会变成N-1,继续走就会变成N-2,N-3…,1,0.当两者之间的节点个数变为0那么就表示两者相遇了,这也可说明这个链表是带环的。所以当快指针走一步慢指针走两步(带环的链表),那么它们一定会在环中相遇。

如果快指针走三步,慢指针还是走一步那么它们是否还会再环中相遇吗?结论是它们也一定会在环中相遇。

在这里插入图片描述
假设这个环是顺时针方向走的,当slow进入到带环的第一个节点,此时slow距离fast的节点个数为N,这个环的大小为C,示意图如上所示。第一次fast走三步,slow走两步,这时两者之间的距离为N-2,继续走依次次为N-4…,当N为偶数是N-6,…,4,2,0。此时两者必定会在环中相遇。
当N为奇数时,两者之间的距离依次为N-6,…,5,3,1,-1。当为-1时表示fast追过了,其示意图如下:在这里插入图片描述
这就表示进行到了新一轮的追击中了,此时fast距离slow相差C-1个节点(从顺时针方向看),当C为奇数那么C-1就是偶数,那么距离的变化一定是C-3,…,4,2,0。所以此时一定也能相遇。
当C为偶数时,那就表示追不上,但是不存在这个情况(同时C为偶数并且N为奇数)。分析如下:
在这里插入图片描述
首先假设slow进环前走的距离为L,那么fast所走的距离为L+xC+C-N,其中x表示在环形链表中所转的圈数。又由于fast所走的距离为slow的三倍,所以有等式L+xC+C-N=3L,所以就有2L=(x+1)*C-N。左边等式一定是偶数,则右边也一定为偶数,假设C为偶数那么则N一定也是偶数,不然就不满足左边是偶数右边不是偶数了。只有当N为奇数,C为偶数才会永远无法再环中相遇,但这种情况不存在。 所以这也表示当fast走三步,slow走一步也一定会在环中相遇。

返回链表开始入环的第一个节点

返回链表开始入环的第一个节点Leetcode
使用快慢指针,快指针走两步慢指针走一步,它们两个一定会在环中相遇。此时一个从相遇点走,另一个从头节点开始走,两者同时走,当两者相遇时,这个相遇的节点就是入环的第一个节点。
根据这个思路代码如下:

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

在这里插入图片描述

为什么一个从相遇点走,应该从头节点走,两者相遇的点就是环形链表环形的入口节点?

ret表示入环的第一个节点,meet表示快慢指针相遇点,head表示链表的头节点。环的大小为C,其示意图如上所示。慢指针到两者相遇所走的距离为L+C-N,快指针在两者相遇所走的距离为L+xC+C-N,由于快指针每次走两步,慢指针每次走一步,所以就有这个关系:2*(L+C-N)=L+xC+C-N,所以就有L=(x-1)C+N,其中x一定大于1。相遇点meet到环形入口节点的距离是N,而头节点到环形链表的入口节点距离是L,所以一个从相遇的节点开始走,另一个节点从头节点开始走,两者一定会在环形链表的入口节点相遇。

另一个思路就是通过寻找链表相交的第一个节点的思路:
依旧是通过快慢指针找到相遇点,再将相遇节点的next置空。这就相当于找链表第一个相交的节点了。
代码如下:

//找两个相交的链表
 struct ListNode *firstcrossnode(struct ListNode *head1,struct ListNode *head2) 
 {
    if(head1==NULL)
        return NULL;
    if(head2==NULL)
        return NULL;
    int len1=0;
    int len2=0;
    struct ListNode *cur1=head1;
    struct ListNode *cur2=head2;
    while(cur1)
    {
        cur1=cur1->next;
        len1++;
    }
    while(cur2)
    {
        cur2=cur2->next;
        len2++;
    }
    int len=abs(len1-len2);
    struct ListNode *longlist=head1;
    struct ListNode *shortlist=head2;
    if(len1<len2)
    {
        longlist=head2;
        shortlist=head1;
    }
    while(len--)
    {
        longlist=longlist->next;
    }
    while(longlist)
    {
        if(longlist==shortlist)
            return longlist;
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
    return NULL;
 }
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *slow=head;
    struct ListNode *fast=head;
    if(fast==NULL||fast->next==NULL)
        return NULL;
    struct ListNode *meet=NULL;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
        {
            meet=slow;
            break;
        }
    }
    if(!fast||!fast->next)
        return NULL;
    struct ListNode*newhead=meet->next;
    meet->next=NULL;//将环形链表切割开
    struct ListNode* cur=head;
    //找第一个相交的链表
    struct ListNode*ret=firstcrossnode(cur,newhead);
    meet->next=newhead;//将链表还原回去
    return ret;
}

总结:本文主要介绍了两个环形链表的经典问题,判断一个链表是否是环形链表以及得到环形链表的入口节点,从公式推到到代码的实现。感谢大家观看,如有错误不足之处欢迎大家批评指针!!!

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

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

相关文章

微软如何打造数字零售力航母系列科普05 - Azure中计算机视觉的视觉指南

Azure中计算机视觉的视觉指南 什么是计算机视觉&#xff1f;如何使用Microsoft Azure将计算机视觉功能集成到应用程序和工作流中&#xff1f; 作者&#xff1a;Nitya Narasimhan 编辑&#xff1a;数字化营销工兵 •11分钟阅读 什么是计算机视觉&#xff1f;如何使用Microso…

在树莓派安装 rpi-imager

步骤 安装 sudo apt install rpi-imager 我没有更新镜像源&#xff0c; 但是能安装完&#xff0c; 只是花的时间旧点。 烧写镜像 因为我是没有桌面的树莓派系统&#xff0c; 所以我这里执行的指令是不需要显示图形化界面的 man rpi-imager ## man指令查看说明 查看帮助手…

综合性练习(后端代码练习4)——图书管理系统

目录 一、准备工作 二、约定前后端交互接口 1、需求分析 2、接口定义 &#xff08;1&#xff09;登录接口 &#xff08;2&#xff09;图书列表接口 三、服务器代码 &#xff08;1&#xff09;创建一个UserController类&#xff0c;实现登录验证接口 &#xff…

服务运营 | 精选:用药难?用药贵?运筹学与统计学视角下的药物研发与管理

作者设计了一个多阶段博弈论模型来针对罕见病的不同补贴方案&#xff0c;分析政府、联盟、制药商和患者之间的相互作用。 制药商补贴为 α C \alpha C αC&#xff0c;其中 C C C是研发成本&#xff0c; α ∈ [ 0 , 1 ) \alpha \in [0,1) α∈[0,1)是政府总成本的比例。患者补…

vue3 依赖-组件tablepage-vue3 项目公共配置封装

github求⭐ 可通过github 地址和npm 地址查看全部内容 vue3 依赖-组件tablepage-vue3说明文档&#xff0c;列表页快速开发&#xff0c;使用思路及范例-汇总 vue3 依赖-组件tablepage-vue3说明文档&#xff0c;列表页快速开发&#xff0c;使用思路及范例&#xff08;Ⅰ&#…

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…

Android11适配

一、分区存储 1.背景 Android 11 进一步增强了平台功能&#xff0c;为外部存储设备上的应用和用户数据提供了更好的保护。作为这项工作的一部分&#xff0c;平台引入了进一步的改进&#xff0c;以简化向分区存储的转换。 为了让用户更好地控制自己的文件&#xff0c;保护用户…

【华为】华为防火墙双机热备

【华为】华为防火墙双机热备 实验需求实验拓扑配置FW5-M前骤单臂路由和VRRP划分防火墙基本区域部署HRP&#xff08;华为心跳协议&#xff09; FW6-B前骤单臂路由和VRRP划分防火墙基本区域部署HRP&#xff08;华为心跳协议&#xff09; LSW2PC NATSNAT &#xff1a;Easy IPDNAT&…

汽车车灯的材料是什么?汽车车灯的灯罩如果破损破裂破洞了要怎么修复?

汽车车灯的材料主要包括灯罩和灯底座两部分&#xff0c;它们所使用的材料各不相同。 车灯罩的材料主要是透明且具有良好耐热性和耐紫外线性能的塑料。其中&#xff0c;聚碳酸酯&#xff08;PC&#xff09;是一种常用的材料&#xff0c;它具有高抗冲击性、耐化学品腐蚀和优良的…

redis核心数据结构——跳表项目设计与实现(跳表结构介绍,节点类设计,随机层级函数)

跳表结构介绍。跳表是redis等知名软件的核心数据结构&#xff0c;其实现的前提是有序链表&#xff0c;思想的本质是在原有一串存储数据的链表中&#xff0c;间隔地抽出一半元素作为上一级链表&#xff0c;并将抽提出的元素和原先的位置相关联&#xff0c;这样重复下去直到最上层…

使用 Python 和 OpenCV 进行实时目标检测的详解

使用到的模型文件我已经上传了&#xff0c;但是不知道能否通过审核&#xff0c;无法通过审核的话&#xff0c;就只能 靠大家自己发挥实力了&#xff0c;^_^ 目录 简介 代码介绍 代码拆解讲解 1.首先&#xff0c;让我们导入需要用到的库&#xff1a; 2.然后&#xff0c;设…

【数据结构-之八大排序(下),冒泡排序,快速排序,挖坑法,归并排序】

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a;基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 …

【MySQL | 第九篇】重新认识MySQL锁

文章目录 9.重新认识MySQL锁9.1MySQL锁概述9.2锁分类9.2.1锁的粒度9.2.2锁的区间9.2.3锁的性能9.2.4锁的级别 9.3拓展&#xff1a;意向锁9.3.1意向锁概述9.3.2意向锁分类9.3.3意向锁作用&#xff08;1&#xff09;意向锁的兼容互斥性&#xff08;2&#xff09;例子1&#xff08…

Springboot+vue+小程序+基于微信小程序的在线学习平台

一、项目介绍    基于Spring BootVue小程序的在线学习平台从实际情况出发&#xff0c;结合当前年轻人的学习环境喜好来开发。基于Spring BootVue小程序的在线学习平台在语言上使用Java语言进行开发&#xff0c;在数据库存储方面使用的MySQL数据库&#xff0c;开发工具是IDEA。…

主成分分析在R语言中的简单应用:使用mvstats包

在数据科学领域&#xff0c;主成分分析&#xff08;PCA&#xff09;是一种广泛使用的技术&#xff0c;主要用于数据降维和探索性数据分析。PCA可以帮助我们发现数据中的模式&#xff0c;减少数据集的复杂性&#xff0c;同时保持数据中最重要的特征。本文将介绍如何在R语言中使用…

STM32定时器中的编码器接口详解

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 编码器接口简介 2. 旋转编码器简介 3. 正交编码器工作模式 4. 基本结构 5. 编码器工作模式示例 6. 代码示例 6.1 Encoder.c 6.2 Encoder.h 6.3 main.c 1. 编码器接口简介 在STM32中&#xf…

Cisco IOS XE Web UI 权限提升漏洞复现(CVE-2023-20198)

0x01 产品简介 Web UI 是一种基于GUI的嵌入式系统管理工具,能够提供系统配置、简化系统部署和可管理性以及增强用户体验。它带有默认映像,因此无需在系统上启用任何内容或安装任何许可证。Web UI 可用于构建配置以及监控系统和排除系统故障,而无需CLI专业知识。 0x02 漏洞…

路由器的构成

一、路由器简介 路由器是互联网中的关键设备&#xff1a; 连接不同的网络路由器是多个输入端口和多个输出端口的专用计算机&#xff0c;其任务是转发分组&#xff08;转发给下一跳路由器&#xff09;下一跳路由器也按照这种方法处理分组&#xff0c;直到该分组到达终点为止 …

洛谷 P1377:树的序 ← 笛卡尔树

【题目来源】https://www.luogu.com.cn/problem/P1377【题目描述】 众所周知&#xff0c;二叉查找树的形态和键值的插入顺序密切相关。准确的讲&#xff1a; 1.空树中加入一个键值 k&#xff0c;则变为只有一个结点的二叉查找树&#xff0c;此结点的键值即为 k。 2.在非空树中插…

Vue2基础用法及案例

Vue2基础用法及案例 目录 Vue2基础用法及案例导入响应式布局文本指令v-textv-html 属性指令Vue-set()v-ifv-showv-forv-bindv-model 事件指令v-on函数使用 Class 与 Style 绑定数组绑定对象绑定 案例1&#xff1a;切换图片案例2&#xff1a;过滤查找方法补充 导入 Vue2 cdn &…