【数据结构】快慢指针

一、快慢指针的原理

定义:
        快指针:每次移动两步
        慢指针:每次移动一步
终止条件:
        当快指针到达链表末尾时停止
事件复杂度:
        始终为O(n),仅需依次遍历
空间复杂度:
        仅需两个指针变量

二、快慢指针的典型应用场景

1、首先制作一个链表的节点
 

#include <iostream>
#include <vector>struct ListNode {int val;ListNode* next;ListNode(int x) :val(x), next(nullptr){}ListNode(int x, ListNode *_next):val(x),next(_next){}
};//传入列表生成list链
ListNode* makeNodeList(std::vector<int> vec)
{ListNode* outNode = nullptr;for (int i = vec.size() - 1; i >= 0 ; i--){outNode = new ListNode(vec[i],outNode);}return outNode;}//遍历输出链表
void traverseNodeList(ListNode* node)
{while (node){std::cout << node->val << " ";node = node->next;}std::cout << std::endl;
}//回收资源
void reclaim(ListNode* node)
{if (node->next){reclaim(node->next);}delete node;
}int main()
{std::vector<int> vec{ 1,2,3,4,5 };ListNode *node = makeNodeList(vec);traverseNodeList(node);reclaim(node);return 0;
}

2、检测链表是否有环


代码如下:

bool haveLoop(ListNode* node)
{ListNode* fast = node;ListNode* slow = node;while (fast != nullptr && fast->next != nullptr){fast = fast->next->next;slow = slow->next;if (fast == slow){return true;		//快指针追上了满指针,有环}}return false;
}int main()
{ListNode* node5 = new ListNode(5);ListNode* node4 = new ListNode(4, node5);ListNode* node3 = new ListNode(3, node4);ListNode* node2 = new ListNode(2, node3);ListNode* node1 = new ListNode(1, node2);node5->next = node2;if (haveLoop(node1)){std::cout << "有环" << std::endl;}else {std::cout << "没环" << std::endl;}return 0;
}

3、找到链表的中间节点

ListNode* middileNode(ListNode* node)
{ListNode *fast = node, *slow = node;while (fast && fast->next) {		//如果fast的next为nullptr,则下面的next->next就找不第二个next了fast = fast->next->next;slow = slow->next;}return slow;
}int main()
{std::vector<int> vec{ 1,2,3,4,5 };ListNode *node = makeNodeList(vec);ListNode *midNode = middileNode(node);std::cout << "奇数个中间节点是" << midNode->val << std::endl;std::vector<int> vec2{ 1,2,3,4,5,6 };ListNode* node2 = makeNodeList(vec2);ListNode* midNode2 = middileNode(node2);std::cout << "偶数个中间节点是" << midNode->val << std::endl;return 0;
}

4、寻找链表的环入口

代码如下:
(因为用到了前面判断是否时环的函数,所以代码都放这里了)

#include <iostream>
#include <vector>struct ListNode {int val;ListNode* next;ListNode(int x) :val(x), next(nullptr){}ListNode(int x, ListNode *_next):val(x),next(_next){}
};//传入列表生成list链
ListNode* makeNodeList(std::vector<int> vec)
{ListNode* outNode = nullptr;for (int i = vec.size() - 1; i >= 0 ; i--){outNode = new ListNode(vec[i],outNode);}return outNode;}//遍历输出链表
void traverseNodeList(ListNode* node)
{while (node){std::cout << node->val << " ";node = node->next;}std::cout << std::endl;
}//回收资源
void reclaim(ListNode* node)
{if (node->next){reclaim(node->next);}delete node;
}bool haveLoop(ListNode* node)
{ListNode* fast = node;ListNode* slow = node;while (fast != nullptr && fast->next != nullptr){fast = fast->next->next;slow = slow->next;if (fast == slow){return true;		//快指针追上了满指针,有环}}return false;
}ListNode* middileNode(ListNode* node)
{ListNode *fast = node, *slow = node;while (fast && fast->next) {		//如果fast的next为nullptr,则下面的next->next就找不第二个next了fast = fast->next->next;slow = slow->next;}return slow;
}ListNode* entranceNode(ListNode* node)
{if (!haveLoop(node)){return nullptr;				//如果不含有环路,返回空指针}ListNode* fast = node, * slow = node;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (fast == slow){break;}}slow = node;while (fast != slow){fast = fast->next;slow = slow->next;}return slow;
}int main()
{ListNode* node5 = new ListNode(5);ListNode* node4 = new ListNode(4, node5);ListNode* node3 = new ListNode(3, node4);ListNode* node2 = new ListNode(2, node3);ListNode* node1 = new ListNode(1, node2);node5->next = node3;ListNode* node = entranceNode(node1);	//Node节点std::cout << "入口节点值为" << node->val << std::endl;return 0;
}

5、判断回文链表
 
代码如下:

#include <iostream>
#include <vector>struct ListNode {int val;ListNode* next;ListNode(int x) :val(x), next(nullptr){}ListNode(int x, ListNode *_next):val(x),next(_next){}
};//传入列表生成list链
ListNode* makeNodeList(std::vector<int> vec)
{ListNode* outNode = nullptr;for (int i = vec.size() - 1; i >= 0 ; i--){outNode = new ListNode(vec[i],outNode);}return outNode;}//遍历输出链表
void traverseNodeList(ListNode* node)
{while (node){std::cout << node->val << " ";node = node->next;}std::cout << std::endl;
}//回收资源
void reclaim(ListNode* node)
{if (node->next){reclaim(node->next);}delete node;
}bool haveLoop(ListNode* node)
{ListNode* fast = node;ListNode* slow = node;while (fast != nullptr && fast->next != nullptr){fast = fast->next->next;slow = slow->next;if (fast == slow){return true;		//快指针追上了满指针,有环}}return false;
}ListNode* middileNode(ListNode* node)
{ListNode *fast = node, *slow = node;while (fast && fast->next) {		//如果fast的next为nullptr,则下面的next->next就找不第二个next了fast = fast->next->next;slow = slow->next;}return slow;
}ListNode* entranceNode(ListNode* node)
{if (!haveLoop(node)){return nullptr;				//如果不含有环路,返回空指针}ListNode* fast = node, * slow = node;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (fast == slow){break;}}slow = node;while (fast != slow){fast = fast->next;slow = slow->next;}return slow;
}bool isSymmeNode(ListNode* node)
{//用前面的函数找到中间节点ListNode* midNode = middileNode(node);//反转中间节点后面的节点// 321 -> 123// 3 2 1// 3		21// 2 3		1// 1 2 3ListNode* temp;ListNode* Lnode = midNode;ListNode* Rnode = nullptr;while (Lnode){temp = Lnode->next;Lnode->next = Rnode;		//让先放进来的值朝着后面排Rnode = Lnode;Lnode = temp;}//第三步、比较前后的值是否相同while(Rnode){if (node->val != Rnode->val){return false;}else {node = node->next;Rnode = Rnode->next;}}return true;
}int main()
{std::vector<int> vec = { 1,2,3,3,2,1 };ListNode* Node = makeNodeList(vec);if (isSymmeNode(Node)){std::cout << "是回文链" << std::endl;}else {std::cout << "不是回文链" << std::endl;}return 0;
}

6、找到链表的倒数第K个节点

ListNode* getKthFromEnd(ListNode* node, int k)
{ListNode* fast = node, * slow = node;for (int i = 0; i < k; i++){if (fast && fast->next){fast = fast->next;}else {return nullptr;}}while (fast){fast = fast->next;slow = slow->next;}return slow;
}int main()
{std::vector<int> vec = { 1,2,3,4,5,6 };ListNode* Node = makeNodeList(vec);std::cout << "倒数第3节点的值是" << getKthFromEnd(Node, 3)->val << std::endl;return 0;
}

三、快慢指针在实际开发中的应用
        下面是DeepseeK给出的,个人认为是从判断环,找中点,业务需求速率不同这三个角度演化。还没实践过。后面有实践应该会贴链接在这里。

        PS:夜已深,学生时期听的歌真有品味啊,当世界年轻时

1. 资源管理与循环检测

  • 场景:在内存管理、文件系统或网络协议中,需要检测资源引用是否形成循环依赖。

    • 示例

      • 垃圾回收(GC):某些垃圾回收算法需要检测对象之间的循环引用(如标记-清除算法)。通过快慢指针可以高效判断对象引用链中是否存在环,避免内存泄漏。

      • 文件系统的符号链接:检测符号链接是否形成环(如 ln -s file1 file2; ln -s file2 file1),避免程序陷入死循环遍历。


2. 数据结构的维护与优化

  • 场景:在自定义链表结构或缓存系统中,需要高效操作链表。

    • 示例

      • 缓存淘汰策略:在实现类似 LRU(最近最少使用)缓存时,若需手动管理链表(而非使用现成库),快慢指针可用于快速定位中间节点或批量调整缓存顺序。

      • 链表合并与分割:在分布式系统中合并多个有序链表(如日志合并),通过快慢指针找到中点后分治处理(类似归并排序)。


3. 网络与协议处理

  • 场景:处理网络数据包的顺序或超时检测。

    • 示例

      • 心跳检测:在长连接中,若用链表维护心跳包的时间戳,快指针(高频检测)和慢指针(低频统计)可协同判断连接健康状态。

      • 数据包乱序重组:通过快慢指针分离已确认和待确认的数据包链表。


4. 并发与多线程编程

  • 场景:在无锁数据结构或任务调度中,检测潜在的死锁或竞态条件。

    • 示例

      • 任务队列管理:在生产者-消费者模型中,快指针快速推进任务处理,慢指针统计未处理任务量,动态调整队列负载。

      • 死锁检测:若线程等待关系形成链表式依赖,快慢指针可辅助检测循环等待(需结合线程调度器的元数据)。


5. 系统性能分析与调试

  • 场景:在性能分析工具或调试器中,追踪程序执行路径。

    • 示例

      • 调用链分析:在分布式追踪系统(如 OpenTelemetry)中,若调用链因异常形成环(如 A→B→A),快慢指针可快速定位问题节点。

      • 栈溢出检测:通过快慢指针监控递归调用的深度(慢指针记录常规检查点,快指针快速探测栈增长)。


6. 游戏与图形处理

  • 场景:处理动态更新链表结构的数据(如动画帧、物理引擎中的刚体链表)。

    • 示例

      • 动画帧同步:快指针快速遍历待渲染帧,慢指针标记已确认同步的帧节点,避免丢帧或重复渲染。

      • 碰撞检测优化:将物体按空间分组成链表,快慢指针协助快速筛选待检测的物体集合。


7. 数据库与存储引擎

  • 场景:在存储引擎的索引结构(如跳表、B+树叶子节点链表)中优化查询。

    • 示例

      • 跳表(Skip List)维护:快指针快速跨越高层索引,慢指针在底层链表同步移动,平衡查询与更新效率。

      • 范围查询优化:通过快慢指针快速定位查询范围的起点和终点。

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

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

相关文章

毕业论文 | 基于STM32的自动烟雾报警系统设计

基于STM32的烟雾报警系统 一、系统设计原理1. **系统架构**2. **工作原理**二、核心公式与算法1. **MQ-2传感器浓度计算**2. **温度传感器数据处理**3. **校准与滤波**三、关键代码实现1. **ADC初始化与数据读取(以MQ-2为例)**2. **报警逻辑与阈值设置**3. **EEPROM存储阈值*…

Android Gradle插件开发

文章目录 1. Gradle插件是什么2. 为什么需要插件3. 编写插件位置4. 编写插件5. 自定义插件扩展5.1 订阅扩展对象5.2 把扩展添加给Plugin并使用5.3 配置参数5.4 嵌套扩展5.4.1 定义扩展5.4.2 获取扩展属性5.4.3 使用5.4.4 执行5.4.5 输出 6. 编写在单独项目里6.1 新建Module6.2 …

PPIO X OWL:一键开启任务自动化的高效革命

2024年&#xff0c;仅凭一PPIO X OWL&#xff1a;一键开启任务自动化的高效革命篇技术论文&#xff0c;OWL的Github仓库便在24小时斩获了15k Star&#xff0c;成为2024年增速最快的多智能体协作框架&#xff0c;重新定义了任务自动化的效率边界。Camel AI团队开源全栈方案&…

分布式事务,事务失效,TC事务协调者

1. 概述 本方案书旨在解决分布式系统中事务一致性问题&#xff0c;重点阐述全局事务标识&#xff08;XID&#xff09;的传递与存储机制、事务协调者&#xff08;TC&#xff09;的设计与部署&#xff0c;以及分布式事务失效场景的应对策略。基于业界成熟框架&#xff08;如Seat…

2025年“深圳杯”数学建模挑战赛D题-法医物证多人身份鉴定问题

法医物证多人身份鉴定问题 小驴数模 犯罪现场法医物证鉴定是关系到国家安全、公共安全、人民生命财产安全和社会稳定的重大问题。目前法医物证鉴定依赖DNA分析技术不断提升。DNA检验的核心是STR&#xff08;Short Tandem Repeat&#xff0c;短串联重复序列&#xff09;分析技术…

Mysql查询异常【Truncated incorrect INTEGER value】

文章目录 异常原因分析1、数据类型不一致2、数据长度超长3、数据格式要正确 处理方案模拟案例创建表数据查询 异常 在执行MySQL的语句时&#xff0c;在控制台报错如下所示。 Data truncation: Truncated incorrect INTEGER value 原因分析 1、数据类型不一致 必须要保证数据…

WPF性能优化举例

WPF性能优化集锦 一、UI渲染性能优化 1. 虚拟化技术 ​​ListView/GridView虚拟化​​: <ListView VirtualizingStackPanel.IsVirtualizing="True"VirtualizingStackPanel.VirtualizationMode="Recycling"ScrollViewer.IsDeferredScrollingEnabled=…

C# 面向对象实例演示

C# 面向对象编程实例演示 一、基础概念回顾 面向对象编程(OOP)的四大基本特性&#xff1a; ​​封装​​ - 将数据和操作数据的方法绑定在一起​​继承​​ - 创建新类时重用现有类的属性和方法​​多态​​ - 同一操作作用于不同对象产生不同结果​​抽象​​ - 简化复杂系…

大连理工大学选修课——机器学习笔记(3):KNN原理及应用

KNN原理及应用 机器学习方法的分类 基于概率统计的方法 K-近邻&#xff08;KNN&#xff09;贝叶斯模型最小均值距离最大熵模型条件随机场&#xff08;CRF&#xff09;隐马尔可夫模型&#xff08;HMM&#xff09; 基于判别式的方法 决策树&#xff08;DT&#xff09;感知机…

蒋新松:中国机器人之父

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 蒋新松:中国机器人之父 一、生平简介 1. 早年经历与求学道路 蒋新松出生于1931年8月3日,江苏省江阴澄北镇一个靠近长江的小镇。他的名字来源于杜…

表征(Representations)、嵌入(Embeddings)及潜空间(Latent space)

文章目录 1. 表征 (Representations)2. 嵌入 (Embeddings)3. 潜空间 (Latent Space)4. 关系总结5. 学习思考 1. 表征 (Representations) 定义: 表征是指数据的一种编码或描述形式。在机器学习和深度学习中&#xff0c;它特指模型在处理数据时&#xff0c;将原始输入数据转换成…

【STM32实物】基于STM32的RFID多卡识别语音播报系统设计

演示视频: 基于STM32的RFID多卡识别语音播报系统设计 前言:本项目可实现多个电子标签IC卡RFID识别,刷卡识别后进行中文语音播报反馈,同时进行控制对应的灯光开关。以此也可扩展开发更多功能。 本项目所需主要硬件包括:STM32F103C8T6最小系统板、RFID-RC522模块、五个IC电…

全面了解CSS语法 ! ! !

CSS&#xff08;层叠样式表&#xff09;是网页设计的灵魂之一&#xff0c;它赋予了网页活力与美感。无论是为一个简单的个人博客增添色彩&#xff0c;还是为复杂的企业网站设计布局&#xff0c;CSS都是不可或缺的工具。那么&#xff0c;CSS语法到底是什么样的呢&#xff1f;它背…

青少年抑郁症患者亚群结构和功能连接耦合的重构

目录 1 研究背景及目的 2 研究方法 2.1 数据来源与参与者 2.1.1 MDD患者&#xff1a; 2.1.2 健康对照组&#xff1a; 2.2 神经影像分析流程 2.2.1 图像采集与预处理&#xff1a; 2.2.2 网络构建&#xff1a; 2.2.3 区域结构-功能耦合&#xff08;SC-FC耦合&#xff09…

【QT】编写第一个 QT 程序 对象树 Qt 编程事项 内存泄露问题

目录 1. 编写第一个 QT 程序 1.1 使用 标签 实现 1.2 纯代码形式实现 1.3 使用 按钮 实现 1.3.1 图形化界面实现 1.3.2 纯代码形式实现 1.4 使用 编辑框 实现 1.4.1 图形化界面实现 1.4.2 纯代码形式实现 1.4.3 内存泄露 2. 认识对象模型&#xff08;对象树&…

在pycharm中创建Django项目并启动

Django介绍 Django 是一个基于 Python 的开源 Web 应用框架&#xff0c;采用了 MTV&#xff08;Model - Template - View&#xff09;软件设计模式 &#xff0c;由许多功能强大的组件组成&#xff0c;能够帮助开发者快速、高效地创建复杂的数据库驱动的 Web 应用程序。它具有以…

在Carla中构建自动驾驶:使用PID控制和ROS2进行路径跟踪

机器人软件开发什么是 P、PI 和 PID 控制器&#xff1f;比例 &#xff08;P&#xff09; 控制器比例积分 &#xff08;PI&#xff09; 控制器比例-积分-微分 &#xff08;PID&#xff09; 控制器横向控制简介CARLA ROS2 集成纵向控制横向控制关键要点结论引用 机器人软件开发 …

【KWDB 创作者计划】_深度解析KWDB存储引擎

文章目录 每日一句正能量引言一、存储引擎核心模块结构二、写前日志 WAL&#xff08;Write-Ahead Log&#xff09;三、列式压缩存储&#xff08;Columnar Compression&#xff09;四、索引机制与混合查询调度五、分布式核心功能&#xff1a;租约管理实战六、时间序列数据处理&a…

Apache Tomcat 漏洞(CVE-2025-24813)导致服务器面临 RCE 风险

CVE-2025-24813Apache Tomcat 中发现了一个严重安全漏洞,标识为,该漏洞可能导致服务器面临远程代码执行 (RCE)、信息泄露和数据损坏的风险。 此缺陷影响以下版本: Apache Tomcat11.0.0-M1通过11.0.2Apache Tomcat10.1.0-M1通过10.1.34Apache Tomcat9.0.0-M1通过9.0.98了解 …

全面解析SimHash算法:原理、对比与Spring Boot实践指南

一、SimHash算法概述 SimHash是一种局部敏感哈希算法&#xff0c;由Google工程师Moses Charikar提出&#xff0c;主要用于海量文本的快速去重与相似度检测。其核心思想是将高维特征向量映射为固定长度的二进制指纹&#xff08;如64位&#xff09;&#xff0c;通过计算指纹间的…