经典面试题---环形链表

1. 环形链表1. - 力扣(LeetCode)

 

要解决这道题,我们首先要挖掘出带环的链表与不带环的链表之间的差别。

以此,才能设计出算法来体现这种差别并判断。

二者最突出的不同,就是不带环的链表有尾结点,也就是说在访问到某个结点时,其next指针可能为NULL。

而带环的链表则没有尾结点,负责访问链表的指针进入环以后,就会一直在环中转圈。

 但是,仅靠这个,我们很难解决这个问题。

按以上得到的信息来说,我们会尝试去1. 检查该链表是否有尾结点,或者2. 检查有无结点被重复访问

对于第一种思路,很明显行不通,因为在检查一个带环的链表时,我们会一直访问不到尾结点,但我们也无法找到确切的指标来证明该链表确实没有尾结点。

对于第二种思路,一般来说,我们会想到将已访问过的结点的地址保存起来,每访问一个结点,我们就检查已保存的地址中是否有与新结点地址相同的地址。

第二种思路没有任何问题,确实可以顺利解决这个问题,那么我们来看看进阶要求。

很明显,第二种思路写出的算法的时间复杂度为O(n!),空间复杂度为O(n)。

也就是说还有更妙的办法,或者说带环链表和不带环链表之间还有更加值得利用的区别。

仔细观察就能发现,带环会改变环中结点之间的相对关系。

当链表不带环时,我们可以肯定地说:值为2的结点在值为-4的结点之前。

但是,当链表带环时,我们却可以认为值为-4的结点在值为2的结点之前,因为值为-4的结点的next指针指向的是值为2的结点。

也就是说,带环改变了环中结点之间的前后相对关系。

那么,我们如何使得这种差异体现出来呢?

这使得我们想到快慢指针

假设快慢指针的都从头结点开始移动。

当链表不带环时,快慢指针一旦分开便再无相遇的可能;

当链表带环时,由于环中结点的相对关系被改变,在快慢指针都进入环之后,原本在前的快指针可以认为是在慢指针之后,那么快指针就会不断追击慢指针,最终二者可能相遇。

但是,我们如何确保快指针最后一定会追上慢指针呢?

在追击过程中,二者每次移动,它们之间的距离的减少量就是二者的速度之差。

由于在慢指针刚进入环时,二者之间的距离一定为整数,所以当二者的速度之差为1时,它们就必定相遇。

依据此思路,我们可以设计出如下的算法:

typedef struct ListNode ListNode;

bool hasCycle(struct ListNode *head) {
    ListNode* slow = head;
    ListNode* fast = head;

    while(fast && (fast->next))
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        return true;
    }
    return false;
}

很明显,该算法的时间复杂度为O(n),空间复杂度只有O(1),满足的进阶的要求并明显减小了时间复杂度。

2. 数学分析

完成这道题还不足以让你通过面试,接下来面试官可能会问你:

1. 二者为什么一定会相遇呢?

2. 慢指针每次前进一个结点,快指针每次前进三个结点可以吗?四或五个结点呢?

 第一个问题我们在解题的过程中就已经解决,你只要将思路清晰地阐述即可。

对于第二个问题,要让面试官刮目相看,我们自然不满足于只解决给出的三种情况(对具体情况单独分析也挺麻烦的),我们会尝试给出判断可行的数学公式。

我们假设:

1. 环的周长为c(环有c个结点)。

2. 当慢指针刚进入环时,快指针已经在环中移动的长度为L。

3. 快指针的速度为k(每次移动k个结点)。

4. 慢指针进入环之后,移动n次时与快指针相遇。

 将进入环的第一个结点标记为下标0,随后结点的下标依次递增,直到再次遇到下标为零的结点。

 二者相遇时,快慢指针的位置是相同的。

据此,当二者相遇时,一定满足这个表达式:(nk + L) % c = n % c

假设相遇时,快指针比慢指针多走了m圈,则有:nk + L = n + mc

移项可得:n = (mc - L) / (k - 1)

由于n为整数,所以,如果存在m使得(mc - L) % (k - 1) = 0,我们就可以认为二者一定会相遇。

很明显,当k = 2时,上式一定成立(任何整数都可以整除1),这也就验证了我们之前的结论。

而当k > 2时,上式能否成立则受到c与L的影响,无法保证一定成立。

3. 环形链表2. - 力扣(LeetCode)

 也就是在刚才的基础之上,要求我们求出环中的第一个结点。

这个时候,我们之前想到的第一种思路似乎就能派上用场了:如果找到了重复访问的结点,直接返回该结点的地址即可。

但是,这道题依然有同样的进阶要求:

同样的要求,也就是在提示我们这道题的最佳思路一定是建立在第一题的解法之上。

那么在第一题判断该链表有环之后,我们要如何找到环中第一个结点呢?

不妨先考虑一下,当快慢指针相遇时,二者在哪个位置。

当慢指针刚好进入环时,快指针的坐标为L % c。

按照快指针追击慢指针的角度来看,快慢指针之间相距的距离为c - (L % c)。

每移动一次,二者之间的距离会缩短1,那么二者共会移动c - (L % c)次。

所以,最终慢指针的坐标会停留在c - (L % c)处,也即二者相遇时的坐标。

此时,慢指针要再次到达下标为0的结点(环中第一个结点),需要再移动(L % c)次。

注意到,L = (L % c) + xc(x为整数)。

也就是说,让慢指针移动L次,其也依然会到达下标为0处(多绕x圈)。

可是,此时,我们怎么知道L是多少呢?

仔细观察会发现,头结点到环中第一个结点的距离恰好就是L。

因为快指针的速度是慢指针的两倍,所以,在进环之前,头结点到慢指针的距离与慢指针到快指针的距离是相等的。

在慢指针恰好进入环时,头结点到环中第一个结点的距离就等于慢指针到快指针的距离,即L。

所以,我们只需要让头结点处的指针和相遇处的指针同时开始移动(每次一个结点),最终就会在下标为0的结点处相遇。

依据这个思路,我们可以写出如下代码:

typedef struct ListNode ListNode;

struct ListNode *detectCycle(struct ListNode *head) {
    ListNode* slow = head;
    ListNode* fast = head;

    while(fast && (fast->next))
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            ListNode* meet = slow;
            while(meet != head)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

4. 结语

如果你在面试中遇到了这道题并清晰流畅地答出了本文的内容,那么你的面试就稳了一半了。

如果觉得写的不错就三连支持一下吧!

加纳……

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第71题简化路径

题目: 题解: func simplifyPath(path string) string {stack : []string{}for _, name : range strings.Split(path, "/") {if name ".." {if len(stack) > 0 {stack stack[:len(stack)-1]}} else if name ! "" &am…

Android 系统启动流程源码分析

一、Init进程启动 是一个由内核启动的用户级进程。内核自行启动之后,就通过启动一个用户级程序init的方式,完成引导进程。 启动的代码init.c中的main函数执行过程:system\core\init.c中: 主要下面两个重要的过程: 1…

泰克示波器如何存储CSV文件?

泰克示波器可以用于各种信号的测量和分析。在实际测试中,我们经常需要将示波器采集到的波形数据保存下来,以便后续的处理和分析。泰克示波器提供了多种方法来存储波形数据,其中一种常用的方式是将数据保存为CSV文件。下面将介绍泰克示波器如何…

VINS预积分与误差模型

文章目录 IMU的测量值误差模型IMU预积分真实模型IMU预积分估计模型误差模型普通增量积分中值积分法 参考文献 IMU的测量值误差模型 IMU的测量值误差模型: a ^ t a t R w t g w b a t n a t ω ^ t ω t b ω t n ω t \begin{array}{} {{{\hat a}_t} {a_t…

成功案例(IF=7.3)| 转录组+蛋白质组+代谢组联合分析分析揭示胰腺癌中TAM2相关的糖酵解和丙酮酸代谢重构

研究背景 肿瘤的进展和发展需要癌细胞的代谢重编程,癌细胞能量代谢模式的改变可以满足快速增殖和适应肿瘤微环境的需要。肿瘤微环境(TME)中的代谢状态受到多种因素的影响,包括血管生成、与其他细胞的相互作用和系统代谢。代谢异质…

Python中批量提取[]括号内第一个元素的四种方法

目录 一、引言 二、方法介绍 使用正则表达式(Regular Expression) 使用字符串分割(String Split) 使用ast模块解析字符串为列表 使用JSON模块解析字符串 三、方法比较与选择 四、总结 一、引言 在Python数据处理过程中&a…

探索1688、淘宝、京东搜索商品聚合API接口:一站式电商搜索解决方案

随着电商行业的不断发展,电商平台的数量和商品种类都在快速增长。商家和开发者在运营过程中,需要经常进行跨平台的商品搜索和数据分析。然而,由于各个电商平台的数据接口存在差异,直接对接多个平台不仅效率低下,而且维…

仓库管理员如何入门?仓库管理六大步骤教会你!

新手菜鸟入行,如何做好一个仓库管理员?仓库运营对于许多行业至关重要,例如制造、零售和物流。它们涉及高效、安全地接收、仓储、拣选、包装和运输货物。 跟着这6个步骤做,最慢一个月,最快一周,就能轻松做好…

42.乐理基础-拍号-看懂拍号的意义

到这必然是已经知道 X、Y的意思了: 然后带入数字: 然后念拍号的时候,在国内,百分之九十的地方是从下往上念,念作四二拍,还有百分之十的地方是和国外一样,从上往下念,念作二四拍&…

GPS与精致农业 无人机应用 农业遥感 农业类

全球定位系统是美国国防部主要为满足军事部门对海上、陆地和空中设施进行高精度导航和定位的要求而建立的。GPS系统最基本的特点是以“多星、高轨、高频、测量-测距”为体制,以高精度的原子钟为核心。GPS作为新一代卫星导航与定位系统,不仅具有全球性、全…

大模型外推能力

一、目录 定义如何提高模型的外推能力?分类测评方法各技术点,以及应用模型,优缺点支持模型长上下文的方案「NTK-aware interpolation」的思路是什么?LLM长度外推方案NTK-by-parts的思路是什么?LLM长度外推方案YaRN是怎…

普通组件的注册-局部注册和全局注册

目录 一、局部注册和全局注册-概述 二、局部注册的使用示例 三、全局注册的使用示例 一、局部注册和全局注册-概述 组件注册有两种方式: 局部注册:只能在注册的组件内使用。使用方法:创建.vue文件,在使用的组件内导入并注册。…

编程语言QT、C++、C#、Matlab、SQL Server开发日志总结

目录 引言 正文 1、Qt连接SQL server数据库 2、C#使用chart绘制实时折线图,波形 3、ORACLEXE数据库 4、QT通过ODBC驱动连接Oracle数据库 5、Microsoft SQL Server 2014 安装图解 6、SQL Server 2014应用 7、C/C​​​​​​​ 8、QT…

vue2后台管理项目

一:项目准备 1)拉取模板代码 远程仓库复制到本地仓库. 2)安装后的项目 路径 code 文件夹 会打开vscode的文件夹. 3)安装vetur和eslint插件可以保存时自动修改不规范的地方. 4)App内有一级路由,路由组件导入如果是layout架子,会导入的是文件夹下的index.js没有则导入index.v…

深度学习实战76-基于目标检测YOLOv5模型的迁移学习使用方法,YOLOv5的原理与结构

大家好,我是微学AI,今天给大家介绍一下深度学习实战76-基于目标检测YOLOv5模型的迁移学习使用方法,YOLOv5的原理与结构。YOLOv5(You Only Look Once version 5)是一种先进的目标检测算法,基于深度学习的单阶段目标检测模型。它的主要原理是通过一次前向传播就同时预测图像…

【Python】字典题

题目:输入一段文本,统计每个字符的个数 in_inputinput(“输入:”) dic{} for char in in_input: if char in dic: dic[char]1 # 字典添加键值对的方法,给字典给键和值的方法 else: dic[char]1 print(dic) for key,value in dic.i…

6、随机森林(Random forests)

Random forests started a revolution in machine learning 20 years ago. For the first time, there was a fast and reliable algorithm which made almost no assumptions about the form of the data, and required almost no preprocessing. In today’s lesson, you’ll…

Apache SeaTunnel 正式发布2.3.5版本,功能增强及多个Bug修复

经过两个月的筹备,我们在2.3.4版本基础上进行了新一轮的迭代,本次更新不仅修复了多个关键问题,还引入了若干重要功能增强和性能优化。 在此,我们先提前感谢社区成员的贡献和支持,如果你想升级最新的版本,快…

26 JavaScript学习:JSON和void

JSON 英文全称 JavaScript Object NotationJSON 是一种轻量级的数据交换格式。JSON是独立的语言JSON 易于理解。 JSON 实例 简单的 JSON 字符串实例: "{\"name\": \"Alice\", \"age\": 25, \"city\": \"San Francisco\&…

【陀螺仪JY61P维特智能】通过单片机修改波特率和角度参考的方法

根据官方文档: 修改波特率 1.解锁:FF AA 69 88 B5 1.1延时200ms 2.修改波特率:FF AA 04 06 00 2.1切换已修改的波特率然后重新发送解锁和保存指令 2.2解锁:FF AA 69 88 B5 2.3延时200ms 4.保存: FF AA 00 00 00 XY轴角度参考 角度参考是以传感器当前的实际位置&…