单链表的成环问题

news2025/1/21 16:30:59

       前言:链表成环问题不仅考察双指针的用法,该问题还需要一定的数学推理和分析能力,看似简单的题目实则细思缜密,值得斟酌~

目录

1.问题背景引入-判断链表是否成环:

1.1.正解:快慢指针

1.2 STL的集合判重

2.判断入环点问题

2.1.正解:快慢指针

思路分析

代码实现

2.2.相遇点断开判断相交点

思路分析

代码实现

3.金句频道


1.问题背景引入-判断链表是否成环:

1.1.正解:快慢指针

对于这个经典问题,一般的,我们得最优解法就是使用快慢指针,下面我们先来分析一下原理:

 

1.2 STL的集合判重

STL还是很好用的,只是会在时间和空间上比双指针算法性能差一些,但胜在思路简单清晰

class Solution {
public:
    bool hasCycle(ListNode *head) {
      //快慢指针
      /*
      ListNode *slow=head;
      ListNode *fast=head;
      bool result=false;
      while(fast&&fast->next)
      {
          slow=slow->next;
          fast=fast->next->next;
          if(slow==fast)
           {
               result=true;
               break;
           }
      }
      return result;
      */
      //C++ STL的set
      set<ListNode *>mp;
      ListNode *p=head;
      while(p)
      {
          if(!mp.count(p))
          {
              mp.insert(p);
              p=p->next;
          }
          else
             return true;
      }
      return false;
    }
};

2.判断入环点问题

       如果我们需要判断是否成环并在成环前提下找出环的入口节点呢,诸位有什么好方法吗?可以先停下来思考片刻,

 这个题看起来容易,实际上需要用数学归纳法推出可行性的结论进行处理,我们来看。

2.1.正解:快慢指针

思路分析

我们还是用上面所讲的双指针算法,我们这里先给出结论:

  一个指针从相遇点开始走,一个指针从链表头开始走,它们会在环的入口点相遇.

下面我们来做一下推导证明: 

我们来假设下列条件:

根据上图所示,我们来看fast和slow分别走了多少步?

       首先,我们需要知道,fast和slow指针的相遇,会在slow在环内部转了几圈后再追上吗?

这个显然是不可能的,其原因,就是当slow开始进入环时,fast已经在环中的某个点上,此后slow每走一步,fast都会走slow所到节点的两倍,现在变成了fast从slow的背后追slow,并且,slow每走一步,fast都会从它背后追赶两步,造成了两者之间的距离每次缩小1,由于fast在开始追slow是就已经在环中了,所以不用一个环的长度,fast必然追上slow。

        这样一来就好算了,slow走的距离就是L+x,而fast走的距离就是L+C+x,我们可以根据2*slow=fast列出等量关系

                                            2(L+x)=L+C+x;

可以解得:L=C-x,

我知道你急,但是先别急~

如果我的链表是C=1,L=3呢?3=1-(-2)吗?很显然,这不符合逻辑,这是因为我们忽视了一个问题:当L>>C的时候,fast会在环中转很多次才能等到slow进入环中,我们假设这种情况下fast在slow进入环中之前已经转了n次,那么我们的fast走过的实际距离应该为

                                          fast=L+n*C+x;

     此时我们的等式变为:2*(L+x)=L+n*C+x

可以解得

                                        

                                               L=n*C-x;

我们将上面的结论反馈到图中

 进而,我们就可以得出结论,

一个指针从相遇点开始走,一个指针从链表头开始走,它们会在环的入口点相遇.

代码实现

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {

      ListNode *slow=head;
      ListNode *fast=head;
      while(fast&&fast->next)
      {
          slow=slow->next;
          fast=fast->next->next;
          if(slow==fast)
           {
              ListNode *meet=slow;//相遇节点
              //相遇节点和头结点同时走,相遇到的点就是入环节点
              while(head!=meet)
              {
                  head=head->next;
                  meet=meet->next;
              }
              return head;
           }
      }
         return NULL;
    }
};

2.2.相遇点断开判断相交点

思路分析

       这个思路不难理解,只要我们将相遇点的下一个节点置为NULL,记住相遇点的下一个节点,然后和头结点一起遍历找出地址相同的节点即为入环节点:

代码实现

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {

      ListNode *slow=head;
      ListNode *fast=head;
      while(fast&&fast->next)
      {
          slow=slow->next;
          fast=fast->next->next;
          if(slow==fast)
           {
              //相遇节点
              //cout<<slow->val<<endl;
              //cout<<fast->val<<endl;
              ListNode *meetnext=slow->next; 
              //cout<<meetnext->val<<endl;
              slow->next=NULL;//断开链表,然后开始找两个链表的相交节点
              //这里我为了方便,直接用STL来判重,写个链表求长度函数,然后将长的链表遍历到和短链表长度一致时再同时开始遍历也可以
              set<ListNode*> mp;
              ListNode *p1=head;
              ListNode *p2=meetnext;
              //步骤还可以优化
              while(p1||p2)
              {
                  if(p1&&!mp.count(p1))
                   {
                       mp.insert(p1);
                       p1=p1->next;
                   }
                  else if(p1&&mp.count(p1))
                     return p1;
                  if(p2&&!mp.count(p2))
                   {
                       mp.insert(p2);
                       p2=p2->next;
                   }
                  else if(p2&&mp.count(p2))
                     return p2;
              }  
           }
      }
         return NULL;
    }
};

3.金句频道

想什么呢,向前走啊你

      你可以消沉,也可以抱怨,但希望天一亮,你又能顶住四面八方的压力,继续努力的好好生活。不要总抓着过去的事情不放,对那些受过的委屈和伤害耿耿于怀,以及在那些根本无法改变的人和事上面无谓的消耗自己,我们人生的进步就是我们的能力和我们的梦想一次次匹配。

 

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

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

相关文章

kubespray部署k8s 1.26集群安装指南

Kubespray 是一个自由开源的工具&#xff0c;它提供了 Ansible 剧本(playbook) 来部署和管理 Kubernetes 集群。它旨在简化跨多个节点的 Kubernetes 集群的安装过程&#xff0c;允许用户快速轻松地部署和管理生产就绪的 Kubernetes 集群。 它支持一系列操作系统&#xff0c;包…

Spring的第十五阶段:Spring的事务管理(02)

1、自定义设置回滚异常 rollbackFor和rollbackForClassName回滚的异常 /*** Transactional 表示使用通知( 启用事务 ) <br/>* rollbackFor 属性可以自定义哪些异常可以回滚事务 <br/>* rollbackForClassName 属性可以自定义哪些全类名的异常回滚事务 <br/>…

Java实现八大排序

&#x1f495;“汲取知识&#xff0c;分享快乐&#xff0c;让生命不留遗憾”&#x1f495; &#x1f386;作者&#xff1a;不能再留遗憾了&#x1f386; &#x1f43c;专栏&#xff1a;Java学习&#x1f43c; &#x1f3c0;该文章主要内容&#xff1a;直接插入排序、希尔排序、…

VMware安装Ubuntu系统

VMware安装Ubuntu系统 1.首先选择文件&#xff0c;点击新建虚拟机 2.选择自定义&#xff0c;点击下一步 3.点击下一步 4.选择稍后安装操作系统&#xff0c;点击下一步 5.选择Linus操作系统&#xff0c;版本选择Ubuntu64位&#xff0c;点击下一位 6.自己看图 7. 这里根据自…

「2023 最新」 Github、Gitlab Git 工作流「常用」 git 命令、规范以及操作总结 Rebase

Git commit 规范 关于提交信息的格式&#xff0c;可以遵循以下的规则&#xff1a; feat: 新特性&#xff0c;添加功能fix: 修改 bugrefactor: 代码重构docs: 文档修改style: 代码格式修改test: 测试用例修改chore: 其他修改, 比如构建流程, 依赖管理 Git 基础知识 当我们通过…

ThinkPHP6,视图的安装及模板渲染和变量赋值 view::fetch() ,view::assgin() ,助手函数

ThinkPHP6&#xff0c;视图的安装及模板渲染和变量赋值 tp6视图功能由\think\View类配合视图驱动&#xff08;也即模板引擎驱动&#xff09;类一起完成&#xff0c;新版仅内置了PHP原生模板引擎&#xff08;主要用于内置的异常页面输出&#xff09;&#xff0c;如果需要使用其…

mysql数据库的数据类型 -- 4

目录 数据类型 4.1&#xff1a;数据类型的分类 4.2&#xff1a;数值类型 4.3&#xff1a;字符类型 4.5&#xff1a;enum和set类型 数据类型 4.1&#xff1a;数据类型的分类 数值类型 描述 TINYINT [UNSIGNED]整数&#xff0c;占用1字节SMALLINT [UNSIGNED] 整数&#xf…

springboot+vue滴答拍摄影项目(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的滴答拍摄影项目。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌…

JAVA程序员不得不知道的String类

文章目录 目录 文章目录 前言 String类的重要性: 一.String类简介 二.String底层源码剖析 三.字符串构造 三.字符串的比较 四.String类常用方法 1.字符串查找 2.字符串转化 2.1 大小写转换 2.2 数组转字符串 2.3数值和字符串转化 2.4 格式化 ​编辑 3 字符串替换 ​…

【19】SCI易中期刊推荐——计算机 | 人工智能领域(中科院2区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

实验四 Spark Streaming

实验四 Spark Streaming 1.实验目的 1. 熟悉编写 Spark Streaming 程序处理流数据的方法。 2.实验内容 1. 实时统计贷款金额 模拟解决贷款金额的实时统计问题。假设某外企客户贷款金额数据如下&#xff08;json 格式&#xff09;&#xff0c; 第一项是客户名称&#xff08;…

[Data structure]双链表 | 一文带你了解线性数据结构之一的双链表

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构 双链表 1、简介2、常见操作3、时间复杂度4、代码实现思路总览5、Node6、DoubleLinkedList6.1、添加节点…

设计模式——对象创建模式之工厂模式

文章目录 前言一、“单一职责” 模式二、Factory Method 工厂方法1、动机2、模式定义3、伪代码示例4、结构 总结 前言 一、“单一职责” 模式 通过“对象创建”模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&…

Linux系统之dstat命令的基本使用

Linux系统之dstat命令的基本使用 一、dstat命令介绍1. dstat简介2. dstat特点 二、本次实践介绍1. 本地环境规划2. 本次实践介绍 三、本地环境检查1. 检查操作系统版本2. 查看系统内核版本3. 检查本地yum仓库源状态 三、安装dstat工具1. 搜索dstat软件2. 安装dstat工具3. 查看d…

海康威视 2024届 数字逻辑设计 实习笔试分析

说明 记录一下 5月11日晚&#xff0c;做的海康威视的一场笔试。分享给需要的IC人。 岗位&#xff1a;数字逻辑设计工程师&#xff08;浙江 杭州&#xff09; 转载需要本人同意&#xff01; 我的见解不一定都是准确的&#xff0c;欢迎评论区交流指正~~ 单选题 1、&#xff…

springboot+vue漫画之家系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的漫画之家系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

TweenMax介绍

GSAP 之 TweenMax 介绍&#xff08;一&#xff09; 一、背景 GreenSock &#xff08;绿袜子&#xff09; GreenSock 是一家做 专业级 JavaScript 动画的公司&#xff0c;主要产品就是其下的 GSAP (GreenSock Animation Platform)&#xff0c;配合着 GSPA 开发了很多专业的动画…

做一个好玩的,给小猫拍照。web 端实现,发布图片,浏览图片。

0&#xff1a;先试试看 hongweizhu.com/#/cat 。 1&#xff1a;上班的路上会路过一家宠物店&#xff0c;里面有一只小猫&#xff0c;给它拍点照片&#xff0c;增加一点乐趣。 2: 使用到的技术 MongoDB 数据库&#xff08;我暂时不想把图片直接放到服务器某个目录上&#xff0c;…

你对这4个ICT行业的网络设备,可能一无所知

晚上好&#xff0c;我是老杨。 上个月给你整了篇安全方向的报告分析&#xff0c;反响不错。 那篇主要是对网络安全的就业前景和怎么入门进行了具体分析&#xff0c;没看过的可以看看&#xff1a;《一不留神&#xff0c;网络安全工程师的岗位需求&#xff0c;破237万了》。 不…

混合精度是如何加速大模型训练的?

混合精度是如何加速大模型训练的&#xff1f; 基础知识回顾float-32从float-32 到float-16 混合精度计算bfloat16 基础知识回顾 float-32 在深度学习中&#xff0c;通常使用float-32 精度的数值训练模型&#xff0c;其中pytorch默认的也是float-32。 float32&#xff0c;也就…