【面试经典150 | 链表】循环链表

文章目录

  • Tag
  • 题目来源
  • 题目解读
  • 解题思路
    • 方法一:哈希集合
    • 方法二:快慢指针
    • 方法三:计数
  • 拓展
  • 其他语言
    • python3
  • 写在最后

Tag

【快慢指针】【哈希集合】【计数】【链表】


题目来源

141. 环形链表


题目解读

判断一个链表中是否存在环。


解题思路

链表中有环,那么在遍历链表中节点的时候就有部分元素被重复遍历,于是可以想到使用一个集合来记录已经遍历的节点,如果集合中有节点在遍历的时候第二次出现,那么一定存在环。该方法看似可以判断环的问题,但是链表中的节点会存在重复值,在无环的链表中也可能存在某些节点值被遍历了两次,导致无环链表被误判成有环链表。

如果集合中存放的是节点而不是节点的值呢?这样就可以唯一表示每一个节点了,因为每一个节点实际上是一个结构体(c语言中)或者类(c++中),都是以地址的形式存在于栈上。

接下来将介绍三种判断链表中是否有环的方法,方法一是哈希集合的方法,方法二是一种数学上的方法,方法三是一种 “巧妙” 的方法。这三种方法都推荐大家认真学习,务必要掌握。

方法一:哈希集合

遍历链表,将经过的节点存到哈希集合中,如果集合中已经存在了该节点,就说明链表中存在环。

实现代码

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:bool hasCycle(ListNode *head) {unordered_set<ListNode*> st;while (head) {if (st.count(head)) {return true;}st.insert(head);head = head->next;}return false;}
};

复杂度分析

时间复杂度: O ( N ) O(N) O(N),其中 N N N 是链表中的节点数。最坏情况下我们需要遍历每个节点一次。

空间复杂度: O ( N ) O(N) O(N),其中 N N N 是链表中的节点数。主要为哈希表的开销,最坏情况下我们需要将每个节点插入到哈希表中一次。

方法二:快慢指针

我们可以使用快慢指针来判断是否有环。定义一个慢指针 slow 每次只移动一个位置,定义一个快指针 fast 每次移动两个位置,两个指针从同一个节点出发:

  • 如果链表中没有环,快指针一定会到达最后的 nullptr 节点;
  • 如果链表中有环,那么两个指针一定会在环内相遇。

实现代码

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:bool hasCycle(ListNode *head) {if (head == nullptr || head->next == nullptr)return false;ListNode *slow = head;ListNode *fast = head->next;while (fast != slow) {if (fast == nullptr || fast->next == nullptr)return false;slow = slow->next;fast = fast->next->next;}return true;}
};

代码中的 while 循环的条件语句是 slow != fast,即快慢指针相等的时候才会退出循环。我们初始化的慢指针为 head,快指针为 head->next,如果我们将两个指针初始都置于 head,那么 while 循环就不会执行,才会对两指针进行上述的初始化,相当于快慢指针是从 dummy head(头结点的虚构的上一个)这个节点同时出发的,我们先让快慢指针出发了各自的一个步幅,这样就可以使用 while 循环了。

当然也可以使用 do...while 循环,这样的话快慢指针初始化为 head 就可以了。

while 语句与 do...while 语句的区别在于前者是先判断再执行,而后者是先执行再判断。

复杂度分析

时间复杂度: O ( N ) O(N) O(N),其中 N N N 是链表中的节点数。

空间复杂度: O ( 1 ) O(1) O(1)

方法三:计数

方法二就比较简单了。如果链表中存在环,那么在遍历链表的时候一定有一些节点值被重复遍历了多次。由于本题中链表最多有 1 e 4 1e4 1e4 个节点,因此我们边遍历节点边计数(从头结点到当前节点的个数),如果数量大于 1 e 4 1e4 1e4,那么链表中一定存在环,我们直接退出迭代遍历的过程,返回 false

实现代码

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:bool hasCycle(ListNode *head) {int cnt = 0;while (head) {++cnt;if (cnt > (int)1e4) {return true;}head = head->next;}return false;}
};

复杂度分析

时间复杂度: O ( N ) O(N) O(N),其中 N N N 是链表中的节点数。我们只有遍历每个节点一次之后才能判断出有没有环。

空间复杂度: O ( 1 ) O(1) O(1)


拓展

本题中如果还需要求有环链表中的入环点,那么该怎么解决呢?也就是 142. 环形链表 II 这个题目要解决的问题。

最简单的方法还是哈希集合的方法,当某个节点第二次出现,该节点就是要求的入环点,该方法很简答这里不予说明。

重点来看一下如何使用 快慢指针法 来求入环点。

先来假设一些变量,入环点之前的距离为 a,入环点到快慢指针第一相遇的点的距离为 b,此时环中剩下的距离为 c,快指针在环中走了 n n n 圈之后,快、慢指针第一次相遇,于是快慢指针行走的位置距离为:

  • 慢指针:a+b
  • 快指针:a+b+n(b+c)

慢指针每次走一个节点位置,快指针每次走两个节点位置,快指针走的路程就是慢指针的两倍,所以快慢指针行走的位置距离有这样的关系:

2 ( a + b ) = a + b + n ( b + c ) 2(a+b)=a+b+n(b+c) 2(a+b)=a+b+n(b+c)

化简得到:

c = a + b + ( b + c ) ( n − 1 ) c = a+b+(b+c)(n-1) c=a+b+(b+c)(n1)

根据以上公式推导可以知道,当入环点到第一次相遇点的距离加上 n − 1 n-1 n1 圈的环的长度等于链表头部到入环点的距离。进一步来讲,如果两个指针分别从头结点和第一次相遇点出发,每次行进一个节点,那么它们相遇的节点(第一次相遇)就是入环点。

实现代码

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *detectCycle(ListNode *head) {if (head == NULL || head->next == NULL || head->next->next == NULL) {return NULL;}ListNode *slow = head->next, *fast = head->next->next;while (slow != fast) {if (fast->next == NULL || fast->next->next == NULL) {return NULL;}slow = slow->next;fast = fast->next->next;}fast = head;while (slow != fast) {slow = slow->next;fast = fast->next;}return slow;}
};

其他语言

python3

以下是 快慢指针法 判断链表中是否有环的python3语言代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = Noneclass Solution:def hasCycle(self, head: Optional[ListNode]) -> bool:if not head or not head.next:return Falseslow = headfast = head.nextwhile fast != slow:if not fast or not fast.next:return Falseslow = slow.nextfast = fast.next.nextreturn True

写在最后

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。

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

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

相关文章

(Java)中的数据类型和变量

文章目录 一、字面常量二、数据类型三、变量1.变量的概念2.语法的格式3.整型变量4.长整型变量5.短整型变量6.字节型变量 四、浮点型变量1.双精度浮点数2.单精度浮点数 五、字符型常量六、布尔型变量七、类型转换1.自动类型转换&#xff08;隐式&#xff09;2.强制类型转换(显式…

react的setState做了什么

1、为什么需要setState setState的作用是帮助我们更改数据的同时并且通知视图进行渲染。因为React并不会绑定视图和state&#xff0c;需要我们手动去更新视图。 2、setState什么时候是同步的&#xff0c;什么时候是异步的 setState这个方法在调用的时候是同步的&#xff0c;…

【人工智能Ⅰ】实验1:谓词表示法与产生式知识表示

实验1 谓词表示法与产生式知识表示 一、实验目的 1、熟悉谓词逻辑表示法&#xff1b; 2、理解和掌握产生式知识表示方法&#xff0c;实现产生式系统的规则库。 二、实验内容 要求通过C/C/python语言编程实现&#xff1a; 1、猴子摘香蕉问题 2、动物识别系统 &#xff08…

Spring无法加载静态属性和SpringBoot单元测试指定加载配置文件

一、Spring无法加载静态属性&#xff0c;怎么解决&#xff1f; Spring主要用于管理和注入Bean&#xff08;对象&#xff09;的实例属性&#xff0c;而不是静态属性。静态属性属于类本身&#xff0c;而不是类的实例&#xff0c;因此Spring的依赖注入机制不会处理它们。 看图&a…

项目部署Linux步骤

1、最小化安装centos7-环境准备 安装epel-release 安装epel-release&#xff0c;因为有些rpm包在官方库中找不到。前提是保证可以联网 yum install -y epel-release 修改IP net-tools net-tool&#xff1a;工具包集合&#xff0c;包含ifconfig等命令 yum install -y net-…

【MySQL-->数据操作】

文章目录 前言一、insert1.单行插入2.多行插入3.插入更新/替换 二、select1.全列查询2.指定列插入3.列别名4. 表达式计算5.去重6.where条件查询7.排序8.limit分页显示 三、update四、delete五、插入查询结果六、聚合函数六、聚合分组1.格式2.where和having的区别 前言 一、inse…

个人创业新机遇,零成本开启真人手办定制项目

桌上的日历变薄&#xff0c;2023年已经接近尾声&#xff0c;浅观这一年&#xff0c;您是否发现大家日常关注的重点有明显的变化&#xff0c;诸多社会事件和宏观数字的背后&#xff0c;潜藏着对经济的“不托底”&#xff0c;而当我们真正开始关注起用个人经济积累去对冲未来的不…

大数据Flink(一百零一):SQL 表值函数(Table Function)

文章目录 SQL 表值函数(Table Function) SQL 表值函数(Table Function) Python UDTF,即 Python TableFunction,针对每一条输入数据,Python UDTF 可以产生 0 条、1 条或者多条输出数据,此外,一条输出数据可以包含多个列。比如以下示例,定义了一个名字为 split 的Pyt…

【会员管理系统】篇二之项目搭建、初始化、安装第三方库

一、项目搭建 1.全局安装vue-cli npm install -g vue/cli查看版本信息 vue -V 2.创建项目 vue create 项目名称 回车 回车 剩余选择如下 之后等待项目创建 最后npm run serve 二、初始化配置 1.更改标题 打开public下的index&#xff0c;将title标签里的改成想要设置的…

汽车电子专有名词与相应技术

1.EEA &#xff08;Electronic & Electrical Architecture 电子电气架构&#xff09; EEA在宏观上概括为物理架构与逻辑架构的结合&#xff0c;微观上通过众多电子元器件的协同配合&#xff0c;或集成式或分布式的系统级电子电气架构&#xff0c;具体详见专栏 新能源汽车电…

contenteditable实现文本内容确认提示

功能需求&#xff1a; 列表进行批量查询&#xff0c;需要对输入的值做提交校验&#xff0c;分三种情况&#xff1a; 若部分字符串有误&#xff0c;部分字符串需要变更字体颜色做提示&#xff0c;再次点击确认则对部分正确数据执行批量查询 若全部数据有误则变更字体颜色做提示&…

redis场用命令及其Java操作

目录 1. Redis入门 1.1 Redis简介 1.2 Redis下载与安装 1.2.1 Redis下载 1.2.2 Redis安装 1.3 Redis服务启动与停止 1.3.1 服务启动命令 1.3.2 客户端连接命令 1.3.3 修改Redis配置文件 1.3.4 Redis客户端图形工具 2. Redis数据类型 2.1 五种常用数据类型介绍 2.2 …

docker(1) dockfile制作docker java镜像 并启动

Dockerfile就是利用固定的指令来描述镜像的结构和构建过程&#xff0c;这样Docker才可以依次来构建镜像&#xff1b; 将springboot项目打包上传到指定目录,并且编写一个dockefile文件放在jar包的同级目录 输入命令 打包镜像 成功打包显示 运行镜像

中国区域2013-2021年森林地上生物量产品

该数据集包括中国陆地区域2013-2021年30m分辨率森林地上生物量产品。每年的产品存放在以年份数字命名的文件夹中&#xff0c;有2013-2021共9个文件夹。 每个文件夹内包括分块的产品和vrt文件。 产品命名方式为分区名年份数字.tif。 不同的分区名称如下&#xff1a; cold te…

简单聊下Redis的主从复制和哨兵机制以及集群(面试题)

ChatGPT的简答&#xff1a; Redis的主从复制&#xff08;Master-Slave Replication&#xff09;是指将一个Redis服务器的数据复制到其他Redis服务器的过程&#xff0c;其中一个服务器作为主节点&#xff08;Master&#xff09;&#xff0c;而其他服务器作为从节点&#xff08;S…

加上boot程序,FreeRTOS就跑不起来了

一、问题描述 bootloader跳转到APP时&#xff0c;app执行完初始化程序后死机 二、分析问题 第一步&#xff0c;执行app时死机死到哪里&#xff1f;通过DEBUG调试发现死到hardfault_handler()函数中&#xff0c;硬件错误&#xff0c;导致硬件错误的原因一般都是中断异常引起的。…

李宇航

该篇文章仅用作能直接在百度搜索到我的csdn,进入我的主页,没有实际意义. 进入李宇航博客方法 通过百度搜索"李宇航" 链接: https://blog.csdn.net/llllyh812 1.电脑端进入方法 输入网址链接: https://blog.csdn.net/llllyh812 或者 进入csdn主页,搜索"李宇…

口袋参谋:实时监测竞争对手的流量来源!

​ 在竞争激烈的淘宝天猫上运营店铺&#xff0c;如果想快速了解竞争对手的销售情况。 则可以通过口袋参谋同类目竞店透视工具&#xff0c;来剖析对方的数据信息&#xff0c;全面了解竞争对手的营销策略。 口袋参谋【同类目竞店透视】功能&#xff1a; 支持一键透视任意竞品…

Rockchip RK3399 - DRM crtc基础知识

一、LCD硬件原理 1.1 CRT介绍 CRT是阴极射线管(Cathode Ray Tube)的缩写,它是一种使用电子束在荧光屏上创建图像的显示设备。CRT显示器在过去很长一段时间内是主流的显示技术,现已被液晶显示屏或其他新兴技术所替代。 在CRT显示器中,扫描电子束从左到右、从上到下移动,照亮…

laravel 中 npm run 同时执行多个命令

在使用laravel 启动项目时 经常需要同时运行两个命令。 1.前端既是 npm run dev 2.后端php则是 php artisan serve 可以安装 使用 concurrently 进行并行启动 concurrently - npm npm install concurrently --save 之后修改 package.json 在 scripts 中增加 &#xff08;多条…