环形链表面试题详解

news2024/10/6 11:47:34

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.coloradmin.cn/o/1643250.html

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

相关文章

Typescript语法二

继承 继承是⾯向对象编程中的重要机制,允许⼀个类(⼦类或派⽣类)继承另⼀个类(⽗类或基类)的属性和⽅法。⼦类可以直接使⽤⽗类的特性,并根据需要添加新的特性或覆盖现有的特性。这种机制赋予⾯向对象程序良…

OpenCV的周期性噪声去除滤波器(70)

返回:OpenCV系列文章目录(持续更新中......) 上一篇:OpenCV如何通过梯度结构张量进行各向异性图像分割(69) 下一篇 :OpenCV如何为我们的应用程序添加跟踪栏(71) 目录 目标 理论 如何消除傅里叶域中的周期性噪声? 源代码 解释 结果 目…

C语言——指针的奥秘(1.0)

指针 一.内存和地址1.内存2.编址 二.指针变量和指针1.取地址操作符(&)2.指针变量和解引用操作符(*)1.指针变量2.拆解指针类型3.解引用操作符4.指针变量的大小 三.指针变量的类型和意义1.指针的解引用2.指针 - 整数3.void* 指针…

54.HarmonyOS鸿蒙系统 App(ArkTS)tcp socket套接字网络连接收发测试

工程代码https://download.csdn.net/download/txwtech/89258409?spm1001.2014.3001.5501 54.HarmonyOS鸿蒙系统 App(ArkTS)tcp socket套接字网络连接收发测试 import socket from ohos.net.socket; import process from ohos.process; import wifiManager from ohos.wifiMana…

redis的哨兵

文章目录 一、概念手动恢复redis主从复制的流程自动回复redis主从复制的流程 二、部署三、哨兵节点的作用演示四、哨兵节点的原理 一、概念 Redis Sentinel 相关名词解释 我主要说一个场景,就是我们上一篇讲到的redis主从复制会遇到的问题,朱姐带你如…

细粒度图像分类论文研读

细粒度图像分类论文研读 摘要Abstract1. 基于细粒度图像分类的视觉语义嵌入模型1.1 文献摘要1.2 创新点1.3 模型网络结构和方法1.3.1 问题陈述1.3.2 两级卷积神经网络1.3.3 局部化 CNN1.3.4 回归排序网络1.3.5 参数学习 1.4 实验1.4.1 数据集1.4.2 实验设置1.4.3 分类结果对比1…

3.SpringSecurity基本原理

SpringSecurity本质是一个过滤器链。十多个过滤器构成一个过滤器链。 这些过滤器在项目启动就会进行加载。每个过滤器执行放行操作才会执行下一个过滤器。 常见过滤器 FilterSecurityInterceptor 是一个方法级的权限过滤器,基本位于过滤器链的最底部。 Excepti…

图神经网络综述和学习路径

应用邻域 应用举例 应用层面(节点,连接,子图,全图) 概念区别 图神经网络本质上解决了表示学习的问题 可以把神经网络看作一个黑箱,图中的f函数 困难与挑战 现代的深度学习,如何把图输入到神经…

Qwik 和 Next.js 未来Web项目框架

Qwikover Next.js Qwik 是我进行 Web 项目开发的首选框架,而不是 Next.js 作为一名全栈工程师,我的前端之旅始于大约 10年前。从纯 JavaScript 和 jQuery 开始,然后转向了 KnockoutJS、AngularJS 和 GWT。React 出现, React 一直是…

IoTDB 入门教程 问题篇②——RPC远程连接IoTDB服务器失败

文章目录 一、前文二、发现问题三、分析问题四、检查6667端口是否监听所有IP五、检查ECS云服务器的安全组是否允许六、检查Linux防火墙是否允许 一、前文 IoTDB入门教程——导读 二、发现问题 使用本地IP127.0.0.1可以连接IoTDB服务器使用远程IPxx.xx.xx.xx却连接不到。提示你…

哥白尼高程Copernicus DEM下载(CSDN_20240505)

哥白尼数字高程模型(Copernicus DEM, COP-DEM)由欧洲航天局(European Space Agency, 简称ESA或欧空局)发布,全球范围免费提供30米和90米分辨率DEM。COP-DEM是数字表面模型(DSM),它表示地球表面(包括建筑物、基础设施和植被)的高程。COP-DEM是经过编辑的D…

java发送请求-http和https

http和https区别 1、http是网络传输超文本协议,client---- http------ server 2、httpshttpssl证书,让网络传输更安全 ,client---- httpssl------ server 3、ssl证书是需要客户端认可的,注意官方证书和jdk生成的证书的用户来使…

sql 中having和where区别

where 是用于筛选表中满足条件的行,不可以和聚类函数一起使用 having 是用于筛选满足条件的组 ,可与聚合函数一起使用 所以having语句中不能使用select中定义的名字

【设计模式】13、template 模板模式

文章目录 十三、template 模板模式13.1 ppl13.1.1 目录层级13.1.2 ppl_test.go13.1.3 ppl.go13.1.4 llm_ppl.go13.1.5 ocr_ppl.go 十三、template 模板模式 https://refactoringguru.cn/design-patterns/template-method 如果是一套标准流程, 但有多种实现, 可以用 template …

nodejs的ws+vue3编写聊天室的demo

nodejs编写ws服务是非常简单高效的,nodejs有众多的实现ws的库,如ws,SocketIO等,nodejs的事件线程是单线程的,所以不要在事件线程内做阻塞性的操作,耗时的操作交给工作线程或者子进程操作。 我使用nodejsvue3实现了写了…

安装vscode基础配置,es6基础语法,

https://code.visualstudio.com/ es6 定义变量 const声明常量(只读变量) // 1、声明之后不允许改变 const PI “3.1415926” PI 3 // TypeError: Assignment to constant variable. // 2、一但声明必须初始化,否则会报错 const MY_AGE /…

MySQL —— 表的基本操作

一、创建 1.语法 create table 表名称( 自定义变量1, 自定义变量2, 自定义变量3(最后一个变量末尾不需要加任何标点符号) )charset字符集 collate校验集 engine存储引擎; ps:若是不具体给字符集、校验集、储存引擎,则采用配置文件…

【网络安全产品】---应用防火墙(WAF)

what Web应用防火墙(Web Application Firewall) WAF可对网站或者App的业务流量进行恶意特征识别及防护,在对流量清洗和过滤后,将正常、安全的流量返回给服务器,避免网站服务器被恶意入侵导致性能异常等问题,从而保障…

计算机网络——Dijkstra路由算法

实验目的 实现基于 Dijkstra 算法的路由软件 实验内容 网络拓扑如图所示 实验过程 先编写开辟应该图的空间,然后给点映射数字,构建图。程序获取用户输入的学号,构建图中边的权值。接下来程序从用户输入获取最短路径的搜索起点&#xff0…

prometheus+grafana的安装与部署及优点

一、Prometheus 的优点 1、非常少的外部依赖,安装使用超简单; 2、已经有非常多的系统集成 例如:docker HAProxy Nginx JMX等等; 3、服务自动化发现; 4、直接集成到代码; 5、设计思想是按照分布式、微服…