【数据结构与算法】(LeetCode)141.环形链表 142.环形链表Ⅱ

【数据结构与算法】(LeetCode)141.环形链表 142.环形链表Ⅱ

文章目录

引言

环形链表问题是数据结构与算法中的经典问题,在面试中出现频率极高。这类问题不仅考察对链表结构的理解,更考验解决问题的思维能力和数学分析能力。本文将详细分析环形链表的判断方法以及环入口节点的定位算法,帮助读者深入理解这一重要问题。

环形链表判断

问题描述

给定一个链表的头节点 head,判断链表中是否存在环。

在这里插入图片描述

解决方案:快慢指针法

快慢指针法是解决环形链表问题的经典方法,其核心思想是使用两个指针以不同速度遍历链表。

bool hasCycle(structListNode*head){structListNode* slow=head,*fast=head;while(fast&&fast->next){ slow=slow->next; fast=fast->next->next;//一定要先让快慢指针走,再判断 因为一开始快慢指针都是headif(slow==fast)return true;}return false;}

原理分析

为什么快慢指针一定能相遇?

假设慢指针进入环时,快指针与慢指针之间的距离为N。由于快指针每次比慢指针多走一步,它们之间的距离会逐次减少:N, N-1, N-2, …, 2, 1, 0。因此最终一定会相遇。

步长选择的数学分析

为什么选择一步和两步?

当快指针每次走三步、四步或更多时,情况会变得更加复杂。以三步为例:

  • 每次移动后,快慢指针之间的距离减少2
  • 当N为偶数时,可以追上
  • 当N为奇数时,会错过,距离变为C-1(C为环长)
在这里插入图片描述

假设slow进环时,fast跟slow的距离是N

fast追击slow时 距离变化为N N-1 N-2 ……2 1 0

所以能追上

在这里插入图片描述

当fast等于3时,每次fast与slow距离就减2

1.当N为偶数时,可以追上

2.当N为奇数时,会错过,这时他们的距离会变为C-1 (假设C为环的长度)

a.如果C-1为偶数(C为奇数),下一轮就追上了

b.如果C-1为奇数(C为偶数),就永远追不上

所以这么一看,追不上的条件是:N为奇数并且C为偶数

在这里插入图片描述

但是这种情况存在吗?

进一步分析表明,当快指针走三步时,追不上的条件是:N为奇数且C为偶数。但通过数学推导可以证明这种情况实际上不可能发生:

假设slow走的距离是L

fast走的距离就是L+x*C+N (x为fast走的圈数)

fast走的距离是slow的3倍

3L=L+x*C+N

化简可得

2L=x*C+N

当C为偶数N为奇数时,x*C为偶数

等号左边是偶数,右边是偶数加奇数(也就是奇数),显然不成立

所以不存在N为奇数,C为偶数的情况

所以fast一定能追上slow

环形链表Ⅱ

题目如下

在这里插入图片描述

方法一

找到fast与slow相遇的位置meet,head与meet同时走,他们相遇位置就是环的入口

证明如下:

在这里插入图片描述

相遇时:

slow走的路程为:L+N

fast走的路程为: L+x*C+N

fast走的是slow的2倍

2(L+N)=L+x*C+N

化简可得

L=(x-1)*C+C-N

所以L的长度就是相遇点转了x-1圈 再走了C-N距离的点

这意味着:从头节点到环入口的距离L,等于从相遇点走(x-1)圈再加(C-N)步。因此,两个指针分别从头节点和相遇点出发,一定会在环入口相遇。

代码如下

structListNode*detectCycle(structListNode*head){structListNode* slow=head,*fast=head;while(fast&&fast->next){ slow=slow->next; fast=fast->next->next;if(slow==fast){structListNode* meet=slow;while(meet!=head){ head=head->next; meet=meet->next;}return meet;}}returnNULL;}

方法二:转换为相交链表问题

算法思路
  1. 找到快慢指针的相遇点
  2. 将环从相遇点处断开
  3. 问题转换为求两个链表的交点问题
在这里插入图片描述

将环形链表断开

newhead=meet->next

meet->next=NULL

这样就转换成了链表相交的问题

structListNode*getIntersectionNode(structListNode*headA,structListNode*headB){structListNode* curA=headA,*curB=headB;int lenA=1,lenB=1;while(curA->next){ curA=curA->next; lenA++;}while(curB->next){ curB=curB->next; lenB++;}int gap=abs(lenA-lenB);//假设法 先假设A长structListNode* longList=headA;structListNode* shortList=headB;if(lenA<lenB){ longList=headB; shortList=headA;}while(gap--){ longList=longList->next;}while(longList){if(longList==shortList)return longList; longList=longList->next; shortList=shortList->next;}returnNULL;}structListNode*detectCycle(structListNode*head){structListNode* slow=head,*fast=head;while(fast&&fast->next){ slow=slow->next; fast=fast->next->next;if(slow==fast){structListNode* meet=slow;structListNode* newhead=meet->next; meet->next=NULL;returngetIntersectionNode(head,newhead);}}returnNULL;}

实际应用与扩展

应用场景

  1. 内存管理:检测内存泄漏或循环引用
  2. 状态机检测:验证有限状态机是否会进入循环状态
  3. 递归检测:判断递归函数是否会无限递归
    meet->next=NULL;
    return getIntersectionNode(head,newhead);
    }}

return NULL;

}

 ## 实际应用与扩展 ### 应用场景 1. **内存管理**:检测内存泄漏或循环引用 2. **状态机检测**:验证有限状态机是否会进入循环状态 3. **递归检测**:判断递归函数是否会无限递归 4. **哈希冲突解决**:在开放定址法中检测哈希表是否已满 

Read more

动态规划 线性 DP 五大经典模型:LIS、LCS、合唱队形、编辑距离 详解与模板

动态规划 线性 DP 五大经典模型:LIS、LCS、合唱队形、编辑距离 详解与模板

文章目录 * 最长上升子序列 * 【模板】最长上升子序列 * 合唱队形 * 牛可乐和最长公共子序列 * 编辑距离 经典线性 dp 问题有两个:最⻓上升⼦序列(简称:LIS)以及最⻓公共⼦序列(简称:LCS),这两道题⽬的很多⽅⾯都是可以作为经验,运⽤到别的题⽬中。⽐如:解题思路,定义状态表⽰的⽅式,推到状态转移⽅程的技巧等等。 因此,这两道经典问题是需要我们重点掌握的。 最长上升子序列 题目描述 题目解析 本题介绍最长上升子序列的一般解法,当数据量不大时用这种解法。 在此之前,小编先区分一下子数组和子序列,子数组需要是连续的,而子序列可以是间断的。 1、状态表示 dp[i]表示以i结尾的所有子序列中,最长的上升子序列。

By Ne0inhk
数据结构:队列

数据结构:队列

前言  本篇文章将讲解队列的概念和结构,队列的实现等知识的相关内容,本章代码实现的知识,与单向链表相关,所以如果还没看过单向链表文章,可以看看: https://blog.ZEEKLOG.net/2401_86982201/article/details/154615762?fromshare=blogdetail&sharetype=blogdetail&sharerId=154615762&sharerefer=PC&sharesource=2401_86982201&sharefrom=from_link 一、队列概念与结构 概念 与栈的数据结构类似,队列:只允许在⼀端进⾏插⼊数据操作,在另⼀端进⾏删除数据操作的特殊线性表,队列具有先进先出FIFO(First In

By Ne0inhk
Flutter 三方库 async_extension 的鸿蒙化适配指南 - 实现具备高级异步编排算法与流操作扩展的并发工具集、支持端侧复杂业务流的函数式处理实战

Flutter 三方库 async_extension 的鸿蒙化适配指南 - 实现具备高级异步编排算法与流操作扩展的并发工具集、支持端侧复杂业务流的函数式处理实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 async_extension 的鸿蒙化适配指南 - 实现具备高级异步编排算法与流操作扩展的并发工具集、支持端侧复杂业务流的函数式处理实战 前言 在进行 Flutter for OpenHarmony 的大规模异步业务系统(如实时行情刷新、多源数据聚合)开发时,如何更优雅地处理 Future 的超时竞争、Stream 的防抖(Debounce)或复杂的并发队列控制?虽然 Dart async 包提供了基础功能,但 async_extension 进一步扩展了异步编程的边界,提供了更符合函数式范式的工具。本文将探讨如何在鸿蒙端构建极致、高效的异步处理链路。 一、原直观解析 / 概念介绍 1.1 基础原理 该库通过对 Dart 核心异步类的非侵入式扩展(Extensions)

By Ne0inhk
链表进阶核心 | LeetCode 92 区间反转:吃透递归反转与哨兵技巧

链表进阶核心 | LeetCode 92 区间反转:吃透递归反转与哨兵技巧

✨链表进阶核心 | LeetCode 92 区间反转:吃透递归反转与哨兵技巧🎯 * 视频地址 * 🚀 开篇引论:链表反转的进阶之路 * 🔄 基础筑基:链表【前n个节点】递归反转 * 1. 函数定义与核心功能 * 2. 递归实现思路拆解 * 3. 直观调用示例 * 4. 关键代码实现(C++)与详解 * 🎯 实战攻坚:LeetCode 92 链表区间反转 * 1. 题目问题描述 * 2. 神器加持:虚拟头节点(哨兵)技巧 * 3. 整体解题思路 * 4. 完整代码实现(C++)与逐行解析 * 5. 算法复杂度分析 * 📚 算法原理深度剖析 * 1. 递归反转的核心原理 * 2. 虚拟头节点的底层逻辑 * 💡 算法学习核心建议 * 结语 * ✅ 关键点回顾 视频地址

By Ne0inhk