环形链表面试题详解

A. 环形链表1

给你一个链表的头节点 head ,判断链表中是否有环.

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况.

如果链表中存在环 ,则返回 true 。 否则,返回 false .
OJ链表

// 判断链表是否有环  
bool hasCycle(ListNode *head) {  if (head == NULL || head->next == NULL) {  // 空链表或只有一个节点的链表不可能有环  return false;  }  ListNode *slow = head;  ListNode *fast = head->next;  // 快慢指针开始移动,直到它们相遇或快指针到达链表尾部  while (slow != fast) {  // 如果快指针到达链表尾部,说明没有环  if (fast == NULL || fast->next == NULL) {  return false;  }  slow = slow->next; // 慢指针每次移动一个节点  fast = fast->next->next; // 快指针每次移动两个节点  }  // 如果快慢指针相遇,说明有环  return true;  
}  

【思路】
快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾

【扩展问题】

  • 为什么快指针每次走两步,慢指针走一步可以?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

  • 快指针一次走3步,走4步,…n步行吗?

假设:快指针每次走三步,慢指针每次走一步, 此时快指针肯定先进环。假设慢指针进环的时候,快指针的位置如图所示
在这里插入图片描述
此时按照上述方法来环绕移动
当快指针每次走三步,慢指针每次走一步时,它们之间的相对速度差是两步。这意味着快指针相对于慢指针每次都会拉近两个节点的距离。但是,如果环的大小(即环中节点的数量)是快指针和慢指针速度差的倍数(即环的大小是2的倍数),那么快指针可能会“套圈”慢指针而不相遇。具体来说,如果环的大小是2的倍数(比如4、6、8等),那么快指针会在到达环的起点之前追上慢指针,但会在慢指针之后再次到达起点,从而不会相遇。

举个例子,假设环的大小是4个节点,快指针每次走三步,慢指针每次走一步。当慢指针走完一圈回到起点时,快指针已经走了三圈,即12个节点,并回到了起点之后两个节点的位置。因此,它们不会在同一位置相遇。

然而,如果环的大小不是快指针和慢指针速度差的倍数,那么快指针最终还是会与慢指针相遇。这是因为在这种情况下,快指针和慢指针的相对位置会在环中不断发生变化,直到它们在某个节点相遇。

但为什么快指针走两步,慢指针走一步可以确保相遇呢?这是因为无论环的大小是多少,快指针都会在环中追上慢指针。具体来说,假设环的大小为n,那么当慢指针走了x圈时(x为正整数),快指针会走了2x圈。由于它们都在环中移动,所以它们最终会在某个节点相遇。

因此,虽然快指针每次走三步或更多步在理论上是可行的(只要处理好空指针和尾部的情况),但在实践中,快指针每次走两步,慢指针走一步是一种更简单、更可靠的方法来检测链表中的环。
所以只有快指针走两步,慢指针走一步才可以,因为换的最小长度是一,即使套圈了两个也在相同的位置.

B. 环形链表2

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null.

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况.

不允许修改链表。
OJ链表

typedef struct ListNode {  int val;  struct ListNode *next;  
} ListNode;  // 函数声明  
ListNode *detectCycle(ListNode *head);  ListNode *detectCycle(ListNode *head) {  if (!head || !head->next) {  // 空链表或只有一个节点的链表没有环  return NULL;  }  ListNode *slow = head;  ListNode *fast = head;  // 第一步:检测环是否存在  while (fast && fast->next) {  slow = slow->next;  fast = fast->next->next;  // 如果快慢指针相遇,说明有环  if (slow == fast) {  break;  }  }  // 如果fast或fast->next为NULL,说明没有环  if (!fast || !fast->next) {  return NULL;  }  // 第二步:找到环的起始节点  slow = head;  while (slow != fast) {  slow = slow->next;  fast = fast->next;  }  // slow(或fast)现在指向环的起始节点  return slow;  
} 

让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。

证明:

  • 定义:
  • 假设环的起始节点为 tail(在环中,我们不知道它的确切位置,但它是环的一部分)。 假设从头节点 head 到环的起始节点tail 的距离为 a 个节点。 假设环的长度(即环中节点的数量)为 b 个节点。 当快慢指针相遇时,假设慢指针走了 s步,那么快指针就走了 2s 步(因为快指针每次移动两步).
  • 快慢指针相遇时的情况:
  • 当快慢指针相遇时,慢指针 slow 已经走了 s 步,其中 s = a + nb(n 是一个非负整数,表示慢指针在环中绕了多少圈)。 同时,快指针 fast 走了 2s 步,即 2s = a + mb(m是另一个非负整数,并且由于快指针走得快,所以 m 会比 n 大).
  • 找出 m 和 n 的关系:
  • 由于快指针走的是慢指针的两倍,所以 2s = s + kb(其中 k 是慢指针在环中多绕的圈数)。 这意味着 s = kb。 所以,当快慢指针相遇时,慢指针在环中绕了 k 圈.
  • 两个指针从不同起点同时移动:
  • 现在,我们有一个指针从头节点 head 开始,另一个从快慢指针相遇点开始。 当从头节点开始的指针走了 a步到达环的起始节点 tail 时,从相遇点开始的指针也在环中走了 k*b 步(因为它之前已经走了这么多步)。 由于环的长度是 b,从相遇点开始的指针现在也在环的起始节点 tail。
  • 结论:
  • 因此,两个指针最终会在环的起始节点 tail 相遇。

伪代码描述:
// 假设 slow 和 fast 已经在环中相遇
slow = head // 重置 slow 到链表头
while (slow != fast) { // 当两个指针没有相遇时
slow = slow->next // 两者同时移动
fast = fast->next
}

// 此时 slow(和 fast)指向环的起始节点
这个逻辑利用了环的性质,确保两个指针最终会在环的起始节点相遇。

在这里插入图片描述
那么看到这就接近尾声了哦,劳动节假期也要接近尾声了,呜呜呜,那么咱们下期再见咯
请添加图片描述

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

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

相关文章

【ZZULIOJ】1096: 水仙花数(函数专题)(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy 提示 code 题目描述 春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的: “水仙花数”是指一个三位数,它的各位数字的立…

【数据结构】第四讲:双向链表

目录 一、链表的分类 二、双向链表的结构及实现 1.带头双向链表的结构 2.创建节点 3.初始化 4.尾插 5.打印 6.头插 7.尾删 8.头删 9.在pos位置之后插入数据 10.删除pos节点 11.查找 12.销毁 个人主页:深情秋刀鱼-CSDN博客 数据结构专栏:数…

虚拟化技术 安装并配置ESXi服务器系统

安装并配置ESXi服务器系统 一、实验目的与要求 1.掌握创建VMware ESXi虚拟机 2.掌握安装VMware ESXi系统 3.掌握配置VMware ESXi系统的管理IP 4.掌握开启VMware ESXi的shell和ssh功能的方法 二、实验内容 1.安装VMware workstation 15或更高版本 2.创建VMware ESXi虚拟…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(三)

本系列课程,将重点讲解Phpsploit-Framework框架软件的基础使用! 本文章仅提供学习,切勿将其用于不法手段! 继续接上一篇文章内容,讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 当我们点击 submit 提…

基于t972 Android9 AP6256,如何在设置中添加5G热点选项,并使其正常打开

通过设置的的WiFi热点选项可以知道关键词“2.4GHz”,因此可以其全局搜索,在packages\apps\Settings\res\values\strings.xml文件下找到如下图所示, 从上面注释可以知道,选项按键选择2.4GHz触发的按键关键词是“wifi_ap_choose_2G…

✔ ★Java项目——设计一个消息队列(五)【虚拟主机设计】

虚拟主机设计 创建 VirtualHost实现构造⽅法和 getter创建交换机删除交换机创建队列删除队列创建绑定删除绑定发布消息 ★路由规则1) 实现 route ⽅法2) 实现 checkRoutingKeyValid3) 实现 checkBindingKeyValid4) 实现 routeTopic5) 匹配规则测试⽤例6) 测试 Router 订阅消息1…

P4_16使用table实现通用的switch分支语句

0 背景 v1.2.1 以及更早版本的P4_16编程语言中,尽管在Control中支持switch分支语句,但是switch中的选择条件是受限的,仅支持 table_name.apply().action_run 作为switch的选择条件。为了解决此限制,将尝试使用P4_16 Tableswitch来…

分布式websocket IM即时通讯聊天开源项目如何启动

前言 自己之前分享了分布式websocket的视频有同学去fork项目了,自己启动一下更方便理解项目嘛。然后把项目启动需要的东西全部梳理出来。支持群聊单聊,表情包以及发送图片。 支持消息可靠,消息防重,消息有序。同时基础架构有分布式权限&…

深入教程:在STM32上实现能源管理系统

引言 能源管理系统(EMS)在提高能源效率、减少能源消耗和支持可持续发展方面起着关键作用。本教程将介绍如何在STM32微控制器上开发一个能源管理系统,这种系统能够监控和控制能源使用,适用于家庭自动化、工业控制系统以及任何需要…

jQuery Moblie 笔记14 开发跨平台移动设备网页

相关内容:jQuery Moblie基础、操作、移动设备仿真器、jQuery Moblie网页实例、jQuery Moblie的UI组件、…… jQuery推出了一套新的函数库jQuery Mobile,目的是希望能够统一当前移动设备的用户界面(UI)。 移动设备开发应用程序目前大致分为两种&#xff…

MLP手写数字识别(3)-使用tf.data.Dataset模块制作模型输入(tensorflow)

1、tensorflow版本查看 import tensorflow as tfprint(Tensorflow Version:{}.format(tf.__version__)) print(tf.config.list_physical_devices())2、MNIST数据集下载与预处理 (train_images,train_labels),(test_images,test_labels) tf.keras.datasets.mnist.load_data()…

第IV章-Ⅰ Vue3组件与组件通信

第IV章-Ⅰ Vue3组件与组件通信 Vue组件根组件全局组件局部组件组件模板父子组件 组件间通信子组件获取父组件数据数据传递选项prop传值校验单向数据流 父组件获取子组件数据emit 方法 多级组件通信provide 声明要传递的数据inject 接收数据 总结应用场景单向数据流(…

强大而简洁:初学Python必须掌握的14个单行代码

Python的魅力与单行代码的重要性 Python,作为一种高级编程语言,自诞生以来就以其简洁、易读、易学的特性而广受开发者喜爱。其魅力不仅在于其强大的功能和广泛的应用领域,更在于其能够用简洁的代码实现复杂的功能,这种能力在很大…

Unity对接科大讯飞实时语音转写WebAPI(Windows平台)

科大讯飞官方文档:实时语音转写 API 文档 | 讯飞开放平台文档中心 (xfyun.cn) 参考文章:unity通过WebAPI连接Websocket实现讯飞语音识别与合成。_unity websocket audio-CSDN博客 要实现语音转文字。首先我们需要从麦克风获取到语音数据,这里…

Redisson 分布式锁和同步器

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 redisson 是基于redis的扩展库,使得redis除了应用于缓存以外,还能做队列…

FloodFill-----洪水灌溉算法(DFS例题详解)

目录 一.图像渲染: 代码详解: 二.岛屿数量: 代码详解: 三.岛屿的最大面积: 代码详解: 四.被围绕的区域: 代码详解: 五.太平洋大西洋水流问题: 代码详解&#x…

基于SpringBoot的教务管理系统 - 源码免费(私信领取)

1. 研究目的 本项目旨在设计并实现一个基于Spring Boot的教务管理系统,以提升学校教务管理效率,优化教学资源配置,提高教学质量,满足学生、教师和管理人员的需求。 2. 研究要求 a. 需求分析 通过调研学校教务管理流程和现有系…

嵌入式单片机中必会的50个电路分享

单片机 电源 声音模块 收音机 485

操作系统-进程管理

1.进程的定义 2.进程的组成 3.进程的特点 4.进程控制结构 5.进程状态 6.进程挂起 6.线程优缺点 7.线程进程的比较 8 .为什么要使用线程 9.用户线程 9.内核线程 10.进程上下文切换信息储存在PCB中 11.fork()

83. 删除排序链表中的重复元素

Problem: 83. 删除排序链表中的重复元素 文章目录 思路解题方法复杂度Code 思路 遍历 解题方法 节点数范围[0, 300] 需要判断节点数是否为0 如果为0则直接返回设置当前节点指针cur,初始化curhead当cur.next非空时: 判断cur.val 与 cur.next.val 是否相等…