LeetCode-141环形链表 LeetCode-142环形链表二

一、前言

本篇文章在我之前讲完的链表、链表与递归的基础上进行讲解,本次我们以leetcode为例,讲解链表的其他题型,今天我们先了解一下环形链表,这里我们以leetCode141和leetCode142为例。

二、LeetCode141

在这里插入图片描述
首先关于这道题,我们知道链表有着天然的递归性质,我们可以借助哈希表,如果链表有环,那么当通过环走到相同节点的时候,在哈希表一定能找到。所以对于递归求解,最基本的问题就是分为有环和无环,如果无环,那么当当前节点等于NULL,直接return false即可。如果有环,那么当hashSet找到此节点时,直接return true即可。然后把head.next作为参数调用递归函数即可。
代码如下:

public static boolean hasCycle(ListNode head) {if (head == null || head.next == null) {return false;}HashSet<ListNode> hashSet = new HashSet<>();return hasCycleRecursion(hashSet,head);}private static boolean hasCycleRecursion(HashSet<ListNode> hashSet, ListNode head) {if(head == null){return false;}if(hashSet.contains(head)){return true;}hashSet.add(head);return hasCycleRecursion(hashSet,head.next);}

在这里插入图片描述
那么这道题有没有更优解呢,答案是有的,这里我们就要介绍一种特殊的算法:快慢指针。
快慢指针的原理,就是让快慢指针同时从head出发,快指针一次走两格,而慢指针一次走一格。
如果链表无环,那么快指针肯定会走到NULL,从而快慢不可能相遇。如果链表有环,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。因为它们的相差速度就是一,当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。还不懂的话,我们以相对速度的角度去看,此时slow不动,fast每次以一的速度去追slow,那么这样肯定是能追上slow的。
在这里插入图片描述
那么这样就很简单了,我们定义slow和fast都从head出发,只要fast.next和fast.next.next均不为空,那么就让slow和fast开始移动,如果在移动过程中slow==fast,那么就说明有环,否则无环:

  public static boolean hasCycle(ListNode head) {if(head == null){return false;}ListNode slow = head;ListNode fast = head;while (fast.next != null && fast.next.next != null){slow = slow.next;fast = fast.next.next;if(fast == slow){return true;}}return false;}

在这里插入图片描述
有些人可能会问,快慢指针为什么快指针一定要是慢指针的两倍,我不可以是三倍吗
其实三倍也可以,只是反而可能拖累效率,推导如下
在这里插入图片描述
如上图,非环长s,环长t,环入口距离相遇点j,假设快指针比慢指针速度快k倍,当两者相遇,此时慢指针走了s+j,快指针走了
s+j+n*t(n为快指针在环中的圈数)。
那么有s+j+n*t=k(s+j),推导一下有(n*t)/(k-1)=s+j,也可以写为(n/k-1)*t = s+j
我们知道这个式子的参数一定都是整数,那么k-1也一定要是正整数,在其他量都确定的情况下,保持(n / k-1)不变的话,分母k-1越小,则分子n也越小,对应快指针在环中走的圈数n也越小,k=2,3,4···,其中k=2时,m最小,即最快相遇,这样效率最高。如果k=3,反而可能影响效率。
故我们快指针设定一次走两步。

三、LeetCode142

那么如果我们需要找到环入口该如何去做呢,拿LeetCode142为例:
在这里插入图片描述
做法一,其实上面的哈希表大家仔细想想,已经做完了这道题,当我发现哈希表重复的节点的时候,那个重复的节点就是环入口。
所以把141的代码稍微改下返回值就可以提交了:

public ListNode detectCycle(ListNode head) {if(head == null || head.next == null){return null;}HashSet<ListNode> listNodes = new HashSet<>();return detectCycleRecursion(listNodes,head);}private ListNode detectCycleRecursion(HashSet<ListNode> listNodes, ListNode head) {if(head == null){return null;}if(listNodes.contains(head)){return head;}listNodes.add(head);return detectCycleRecursion(listNodes,head.next);}

在这里插入图片描述
那么解法二自然还是之前的快慢指针了,我们首先定义快慢指针,然后沿用判环的代码,如果快慢指针相遇,说明有环,此时在这个基础上,我们来推导环的入口:
在这里插入图片描述
设a为head到环入口的距离,环长为t
此时快指针比慢指针多走nt步(因为slow和fast都走了a步),设立f为fast走的,s为slow走的,有f=2s,f=s+nt
由上面的结论可以得到s=nt,即慢指针走了nt距离
假设从链表头部一直向前走并统计步数k,那么所有 走到链表入口节点时的步数 是:k=a+nt
那么根据上面s=nt,头节点走到环入口是a+nt,我们只需要让慢指针再走a步即可达到环入口而头节点距离环入口刚好是a。那么就是头节点和慢指针一起出发,相遇的节点就是环入口
所以当快慢指针相遇的时候,我们定义两个指针,分别从head和slow出发,一次走一步,相遇点就是环入口,代码如下:

	public static ListNode detectCycle(ListNode head) {if(head == null){return null;}ListNode slow = head;ListNode fast = head;while (fast.next != null && fast.next.next != null){slow = slow.next;fast = fast.next.next.next;if(slow == fast){ListNode newSlow = slow;ListNode newHead = head;while (newSlow != newHead){newSlow = newSlow.next;newHead = newHead.next;}return newHead;}}return null;}

在这里插入图片描述
这里最后提一嘴,如果要求环长,首先当然还是可以用hash表,和上面思路一样,当发现一样的元素时,此时看哈希表的长度即可。
而另一种方法就是当快慢指针相遇时,由于此时肯定是有环的,我只需要让慢指针一格一格往前走,快指针不动,当慢指针再次和快指针相遇时,此时慢指针走的距离就是环长。这里代码就不做赘述,比较简单,大家可以自行实现。

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

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

相关文章

微服务注册中心之Eureka

微服务注册中心之Eureka eureka 搭建集群 版本说明 Spring Boot 2.1.7.RELEASE spring-cloud-starter-netflix-eureka-server Finchley.SR2 spring-boot-starter-security 2.1.7.RELEASE pom.xml 文件 <?xml version"1.0" encoding"UTF-8"?> &l…

游戏缺少emp.dll详细修复教程,快速解决游戏无法启动问题

在现代游戏中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“emp.dll丢失”。emp.dll是一个动态链接库文件&#xff0c;它包含了许多程序运行所需的函数和数据。当一个程序需要调用这些函数时&#xff0c;系统会从emp.dll文件中加载相应的内容。因此&#x…

VSCode上远程调试代码出现的问题

记录一下&#xff1a; 真的是汗流浃背了&#xff0c;师妹叫帮忙如何在VSCode上远程调试代码&#xff0c;一些自己已经经历过的问题&#xff0c;现在已经忘记了。又在网上一顿搜索&#xff0c;这次记录下吧。。。 出现以下问题&#xff1a; 1. 终端界面总是sh-4.4 $ &#xff…

【每日试题】java面试题之中间件

什么是中间件&#xff1f; 中间件是指位于客户端和服务器之间的一层软件&#xff0c;它可以提供一系列的服务&#xff0c;简化了开发和管理复杂的分布式应用系统。 中间件的分类有哪些&#xff1f; 中间件可以分为消息中间件、缓存中间件、数据访问中间件、分布式计算中间件、…

LINUX加固之命令审计

一、前言 在LINUX安全范畴中&#xff0c;安全溯源也是很重要的一个环节。对主机上所有曾操作过的命令详细信息需要有一份记录保存&#xff0c;当系统遭受破坏或者入侵&#xff0c;拿出这份记录&#xff0c;可以帮助定位一些可疑动作。 很多系统通常都会配置安全堡垒机&#xff…

jmeter断言-三种

1.响应断言 substring是指包含就行 不用完全相等 2.json断言 3.持续时间断言

Consule安装与SpringBoot集成

Consule Consul 是由 HashiCorp 开发的一款软件工具&#xff0c;提供了一组功能&#xff0c;用于服务发现、配置管理和网络基础设施自动化。它旨在帮助组织管理现代分布式和微服务架构系统的复杂性。以下是Consul的一些关键方面和功能&#xff1a; 服务发现&#xff1a;Consul…

JS常用数据类型转换

js提供了5中基本数据类型&#xff1a;数字 number 字符串 string 布尔 boolean 空值 null 未定义的 undefined 常用的是数字型和字符串型之间的转换&#xff0c;常用的转换方法如下&#xff1a; 1 数字型转换成字符串型 a) 使用String&#xff08;&#xff09;方…

java设计小分队01

1.开发流程&#xff1a; 编辑&#xff1a;生成.java文件编译&#xff1a;javac命令&#xff0c;生成.class文件运行&#xff1a;java命令 2.标识符下列那个&#xff08;不&#xff09;合法&#xff1a; 除了第一个词小写&#xff0c;其他词首字母大写&#xff1b;java标识符为…

Bash脚本中的分支控制:深入理解Case语句

在编写Bash脚本时&#xff0c;我们经常需要根据不同的条件执行不同的代码块。传统的if-elif-else语句在处理多条件分支时可能会变得冗长和复杂。幸运的是&#xff0c;Bash提供了一个更为简洁的选择结构&#xff1a;case语句。在本文中&#xff0c;我们将深入探讨case语句的使用…

2024第一篇: 架构师成神之路总结,你值得拥有

大家好&#xff0c;我是冰河~~ 很多小伙伴问我进大厂到底需要怎样的技术能力&#xff0c;经过几天的思考和总结&#xff0c;终于梳理出一份相对比较完整的技能清单&#xff0c;小伙伴们可以对照清单提前准备相关的技能&#xff0c;在平时的工作中注意积累和总结。 只要在平时…

仅仅几行 Python 代码,却可帮你快手完成大部分工作

Python 作为一种脚本语言&#xff0c;开发简单&#xff0c;几行代码却能发挥大作用。 本文将介绍几种有趣的 Python 脚本&#xff0c;一定能在你的生活和工作中发挥用处。 自动整理文件和文件夹 手动整理文件和文件夹可能很乏味。这个 Python 脚本可按扩展名类型自动将文件排…

NPC问题

1. P 问题和 NP 问题&#xff1a; P 问题&#xff08;多项式时间可解问题&#xff09;&#xff1a; P 问题是可以在多项式时间内有效解决的问题&#xff0c;即存在一个算法&#xff0c;其运行时间是输入规模的多项式函数。例如&#xff0c;排序算法、搜索算法等都属于 P 问题。…

【DevOps-02】Code编码阶段工具

一、简要说明 在code阶段,我们需要将不同版本的代码存储到一个仓库中,常见的版本控制工具就是SVN或者Git,这里我们采用Git作为版本控制工具,GitLab作为远程仓库。 Git安装安装GitLab配置GitLab登录账户二、Git安装 Git官网 Githttps://git-scm.com/

gin框架实战(一)- HTTP请求参数校验之神器validator

1 快速安装 使用之前&#xff0c;首先要获取validator这个库&#xff1a; $ go get github.com/go-playground/validator/v10 2 功能 golang http 请求参数校验工具&#xff0c;具备复杂参数校验规则。 3 操作符 标记 标记说明 , 多操作符分割 | 或操作 - 跳过字段…

重新定义出行,PIX移动空间-Robobus2.0正式发布

PIX从创始之初就以重塑城市作为愿景&#xff0c;基于对未来终局的思考&#xff0c;我们重新定义了下一代汽车–移动空间&#xff0c;汽车不再只是一个交通工具&#xff0c;而是一个个提供服务的移动空间&#xff0c;这也将最终重塑城市&#xff0c;使城市成为一个真正的超级有机…

正定矩阵在格密码中的应用(知识铺垫)

目录 一. 写在前面 二. 最小值点 三. 二次型结构 四. 正定与非正定讨论 4.1 对参数a的要求 4.2 对参数c的要求 4.3 对参数b的要求 五. 最小值&#xff0c;最大值与奇异值 5.1 正定型&#xff08;positive definite&#xff09; 5.2 负定型&#xff08;negative defin…

【MySQL】字符集与排序规则

在MySQL数据库中&#xff0c;字符集&#xff08;Character Set&#xff09;和排序规则&#xff08;Collation,也称字符集校验规则&#xff09;是重要的概念&#xff0c;它们对于正确存储和比较数据至关重要。 字符集与排序规则 字符集是一组字符的集合&#xff0c;与数字编码…

8个流行的Python可视化工具包,你喜欢哪个?

用 Python 创建图形的方法有很多&#xff0c;但是哪种方法是最好的呢&#xff1f;当我们做可视化之前&#xff0c;要先明确一些关于图像目标的问题&#xff1a;你是想初步了解数据的分布情况&#xff1f;想展示时给人们留下深刻印象&#xff1f;也许你想给某人展示一个内在的形…

PostgreSQL从入门到精通 - 第40讲:数据库不完全恢复

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第40讲&#…