当前位置: 首页 > article >正文

【高频面试题】快慢指针及相关应用

文章目录

    • 1 简介
    • 2 相关应用
    • 3 相关题目
    • 4 典型例题
      • 4.1 判断链表是否有环
      • 4.2 寻找链表的入环点
      • 4.3 寻找链表的中点
      • 4.4 寻找链表的倒数第k个节点
      • 4.5 重排链表 (反转链表+找链表中点+合并链表)
      • 4.6 寻找重复数(快慢指针 or 二分)
      • 4.7 回文链表
    • 5 参考

1 简介

快慢指针的解法在面试题中非常常见,尤其是涉及到链表有环的情况下。
我们常说的快慢指针解法(Floyd's Cycle-Finding Algorithm)会在链表的头部初始化两个指针,分别称为快指针和慢指针。通过它们移动速度的差异以及最终相遇的位置(或无法相遇的事实),可以高效地解决一系列问题。本文主要介绍快慢指针要应对的各种题型及相关应用。

2 相关应用

  • 判断链表是否有判环
  • 寻找链表的中点
  • 寻找链表的倒数第k个节点
  • 寻找链表的交点
  • 寻找链表的入环点
  • 计算链表的环的长度

3 相关题目

  • LeetCode141. 环形链表

  • LeetCode142. 环形链表II

  • LeetCode876. 链表的中间结点

  • 面试题 02.02. 返回倒数第 k 个节点

  • LeetCode143. 重排链表

  • LeetCode287. 寻找重复数

  • LeetCode234. 回文链表

4 典型例题

4.1 判断链表是否有环

  • 题目描述:给定一个单向链表的头指针,判断链表中是否有环
  • 解题思路:这是快慢指针最著名、最基础的应用。这里用到两组指针:快指针和满指针,快指针每次移动两步,慢指针每次移动一步。这类似于龟兔赛跑,兔子跑到最后会套乌龟一圈最终相遇,因此,如果有环两个指针一定会相遇。
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)
  • 代码实现如下:
class Solution {
public:// LeetCode141bool hasCycle(ListNode* head) {ListNode* slow = head, *fast = head; while (fast && fast->next) {slow = slow->next; fast = fast->next->next; if (fast == slow)return true;}return false; }
};

4.2 寻找链表的入环点

  • 题目描述:给定一个单向链表的头指针,返回链表开始入环的第一个节点,如果链表无环,则返回 null。(不能修改链表)
  • 解题思路:本题的做法比较巧妙。用两个指针 slow, fast分别从起点开始走,slow每次走一步,fast每次走两步。如果过程中 fast 走到null,则说明不存在环。否则当 fastslow相遇后,让 slow返回起点, fast 待在原地不动,然后两个指针每次分别走一步,当相遇时,相遇点就是环的入口。
  • 推导:假设入环前的路程为 a a a,环长为 b b b。设慢指针走了 x x x步时,快慢指针相遇,此时快指针走了 2 x 2x 2x步。显然 2 ∗ x − x = n ∗ b 2*x-x=n*b 2xx=nb(快指针比慢指针多走 n n n圈),即 x = n ∗ b x=n*b x=nb,也就是慢指针走过的路程为 n ∗ b n*b nb,则慢指针在环中走了 n ∗ b − a n*b-a nba步,他还要再往前走 a a a步才是完整的 n n n圈。所以,我们让头节点和慢指针同时往前走,当他们相遇时,刚好走过这 a a a步。
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)
  • 代码实现如下:
class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode *p = head, *q = head;while (q && q->next) {p = p->next;q = q->next->next;if (p == q) {ListNode *tmp = head;while (tmp != p) {tmp = tmp->next;p = p->next;}return tmp;}}return nullptr;}
};

4.3 寻找链表的中点

  • 题目描述:给定一个单向链表的头指针,找到链表的中点
  • 解题思路:快慢指针,快指针每次走两步,慢指针每次走一步,使快指针走的距离始终是慢指针的两倍, 当快指针走到链表尾部时,慢指针正好走到链表中间。
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)
  • 代码实现如下:
class Solution {
public:// LeetCode876ListNode* middleNode(ListNode* head) {ListNode *slow = head, *fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;}return slow;}
};

4.4 寻找链表的倒数第k个节点

  • 题目描述:给定一个单向链表的头指针,找到链表的中点
  • 解题思路:快慢指针,先让一个指针走k步,然后两个一起走,使得fast与slow始终保持k步距离,当fast到尾节点时,slow便指向倒数第k个节点。
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)
  • 代码实现如下:
class Solution {
public:int kthToLast(ListNode* head, int k) {ListNode *slow = head, *fast = head;while (k --) {fast = fast->next;}while (fast) {slow = slow->next;fast = fast->next;}return slow->val;}
};

4.5 重排链表 (反转链表+找链表中点+合并链表)

  • 题目描述:给定一个链表 L : L 0 → L 1 → … → L n − 1 → L n L:L0→L1→…→Ln−1→Ln L:L0L1Ln1Ln,将它变成 L 0 → L n → L 1 → L n − 1 → L 2 → L n − 2 → … L0→Ln→L1→Ln−1→L2→Ln−2→… L0LnL1Ln1L2Ln2
  • 解题思路:先将找到后半部分链表,再将其反转,最后从head开始依次插入更新节点。
  • 时空复杂度:时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)
  • 代码实现如下:
class Solution {
public:// 反转链表ListNode* reverseList(ListNode* head) {ListNode *prev = nullptr;ListNode *cur = head;while (cur) {ListNode *next = cur->next;cur->next = prev;prev = cur, cur = next;}return prev;}// 链表的中间节点ListNode* middleNode(ListNode* head) {ListNode *slow = head, *fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;}return slow;}void reorderList(ListNode* head) {ListNode *mid = middleNode(head); // 找出后半部分ListNode *head2 = reverseList(mid); // 反转后半部分while (head2->next) { // 注意这个条件 mid后面长度奇偶都可以// 保存下一个节点ListNode *nxt = head->next;ListNode *nxt2 = head2->next;// 连接head->next = head2;head2->next = nxt;// 移至nexthead = nxt;head2 = nxt2;}}
};

也可参考官方解法,将后半的链表断开再合并

class Solution {
public:// 反转链表ListNode* reverseList(ListNode* head) {ListNode *prev = nullptr;ListNode *cur = head;while (cur) {ListNode *next = cur->next;cur->next = prev;prev = cur, cur = next;}return prev;}// 链表的中间节点(返回中间节点的前一个节点)ListNode* middleNode(ListNode* head) {ListNode* slow = head;ListNode* fast = head;while (fast->next && fast->next->next) {slow = slow->next;fast = fast->next->next;}return slow;}void mergeList(ListNode *head1, ListNode *head2) {while (head1 && head2) {ListNode *p = head1->next, *q = head2->next;head1->next = head2;head1 = p;head2->next = head1;head2 = q;}}void reorderList(ListNode* head) {if (head == nullptr) return;ListNode* mid = middleNode(head); // 中间节点ListNode* l1 = head; ListNode* l2 = mid->next;		  mid->next = nullptr;l2 = reverseList(l2);			  // 中间节点之后的全部反转mergeList(l1, l2);				  // 合并}
};

4.6 寻找重复数(快慢指针 or 二分)

  • 题目描述:给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1n),可知至少存在一个重复的整数。假设nums只有 一个重复的整数 ,返回这个重复的数 。你设计的解决方案必须 不修改 数组 nums 且只用常量级 O ( 1 ) O(1) O(1) 的额外空间。
  • 解题思路:
    • 快慢指针:我们对nums数组建图,每个位置 i 连一条 i→nums[i] 的边。由于存在的重复的数字 target,因此 target 这个位置一定有起码两条指向它的边,因此整张图一定存在环,且我们要找到的target 就是这个环的入口,那么整个问题就等价于求链表环的入口。以[1, 3, 4, 2, 2]为例,构建0->1, 1->3, 2->4, 3->2, 4->2构成1->3->2->4->2这就形成了一个2->4->2->...的环。时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)
    • 二分查找:因为在区间[1, n]存在重复的数,且只有一个数字是出现多次的,其余数字要么出现,要么不出现。设cnt表示某一数字num在数组中出现的次数,那么当cnt>numnum是从前往后第一个满足这个条件的数字时,该数字是要寻找的重复数,所以采用二分查找找第一个cnt大于本身数值的数。时间复杂度 O ( l o g n ) O(logn) O(logn), 空间复杂度 O ( 1 ) O(1) O(1)
  • 代码实现如下:
class Solution {
public:int binarySearch(vector<int> &nums) {int n = nums.size();// 二分 第一个cnt大于本身数值的数int left = ranges::min(nums) - 1, right = ranges::max(nums) + 1;while (left + 1 < right) {int mid = left + (right - left) / 2;int cnt = 0;for (auto &x : nums) cnt += x <= mid;if (cnt > mid) right = mid;else left = mid;}return right;}int findDuplicate(vector<int>& nums) {int fast = 0, slow = 0;do {slow = nums[slow];fast = nums[nums[fast]];} while (fast != slow);slow = 0;while (slow != fast) {slow = nums[slow];fast = nums[fast];}return slow;}
};

4.7 回文链表

  • 题目描述:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false
  • 解题思路:
    • 快慢指针:找中间节点+反转链表,时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( 1 ) O(1) O(1)
    • 栈:时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( n ) O(n) O(n)
  • 代码实现如下:
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:// 找链表中间节点ListNode *middleNode(ListNode *head) {ListNode *slow = head, *fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;}return slow;}// 反转链表ListNode *reverseList(ListNode *head) {ListNode *pre = nullptr, *cur = head;while (cur) {ListNode * nxt = cur->next;cur->next = pre;pre = cur;cur = nxt;}return pre;}// 栈bool check(ListNode *head) {stack<int> st;ListNode *p = head;while (p) {st.push(p->val);p = p->next;}while (head) {if (head->val != st.top()) {return false;}head = head->next;st.pop();}return true;}bool isPalindrome(ListNode* head) {ListNode *mid = middleNode(head);ListNode *p = reverseList(mid);while (p) {if (p->val != head->val) return false;p = p->next;head = head->next;}return true;//return check(head);}
};

5 参考

  • 一文看懂快慢指针解法
  • 环形链表II【基础算法精讲 07】
  • 【图解】一张图秒懂环形链表 II(Python/Java/C++/C/Go/JS)
  • LeetCode 142. Linked List Cycle II

相关文章:

【高频面试题】快慢指针及相关应用

文章目录 1 简介2 相关应用3 相关题目4 典型例题4.1 判断链表是否有环4.2 寻找链表的入环点4.3 寻找链表的中点4.4 寻找链表的倒数第k个节点4.5 重排链表 &#xff08;反转链表找链表中点合并链表&#xff09;4.6 寻找重复数&#xff08;快慢指针 or 二分&#xff09;4.7 回文链…...

sudo docker exec -it backend bash 以交互方式(interactive)进入正在运行的 Docker 容器的命令行环境

sudo docker exec -it backend bash&#x1f50d; 总体作用 这条命令的作用是&#xff1a; 以交互方式&#xff08;interactive&#xff09;进入名为 backend 的正在运行的 Docker 容器的命令行环境。 你会进入容器的“终端”&#xff0c;就像登录到一个 Linux 系统一样&#…...

[论文阅读] 人工智能 | 当AI遇见绿色软件工程:可持续AI实践的研究新方向

【论文解读】当AI遇见绿色软件工程&#xff1a;可持续AI实践的研究新方向 论文信息 作者&#xff1a;Maja H. Kirkeby, Enrique Barba Roque, Justus Bogner等 标题&#xff1a;Greening AI-enabled Systems with Software Engineering: A Research Agenda for Environment…...

[论文阅读] 人工智能 | 用大语言模型抓虫:如何让网络协议实现与RFC规范对齐

用大语言模型抓虫&#xff1a;如何让网络协议实现与RFC规范对齐&#xff1f; 论文信息 arXiv:2506.01249 SysLLMatic: Large Language Models are Software System Optimizers Huiyun Peng, Arjun Gupte, Ryan Hasler, Nicholas John Eliopoulos, Chien-Chou Ho, Rishi Mantr…...

浅析EXCEL自动连接PowerBI的模板

浅析EXCEL自动连接PowerBI的模板 之前我分享过&#xff1a;PowerBI链接EXCEL实现自动化报表 &#xff0c;其中一个关键工具就是提到的EXCEL链接模板&#xff0c;即宏工作薄。 今天就大概来聊一聊这个宏工作簿的底层原理是啥&#xff0c;怎么实现的。 第一步&#xff1a; 打开…...

DeepSeek 赋能金融反洗钱:AI 驱动的风险监测革新之路

目录 一、引言二、金融反洗钱监测的现状与挑战2.1 现状概述2.2 面临的挑战 三、DeepSeek 技术原理剖析3.1 核心架构3.2 关键技术 四、DeepSeek 在金融反洗钱监测中的应用优势4.1 强大的数据处理与分析能力4.2 精准的风险识别与预警4.3 提升工作效率与降低成本 五、DeepSeek 在金…...

java32

1.反射 获取类&#xff1a; 获取构造方法&#xff1a; 获取权限修饰符&#xff1a; 获取参数信息&#xff1a; 利用反射出来的构造器来创建对象&#xff1a; 获取成员变量&#xff1a; 获取成员方法&#xff1a; 综合练习&#xff1a; 动态代理&#xff1a;...

【Redis】zset 类型

zset 一. zset 类型介绍二. zset 命令zaddzcard、zcountzrange、zrevrange、zrangebyscorezpopmax、zpopminzrank、zrevrank、zscorezrem、zremrangebyrank、zremrangebyscorezincrby阻塞版本命令&#xff1a;bzpopmax、bzpopmin集合间操作&#xff1a;zinterstore、zunionstor…...

从Gartner报告看Atlassian在生成式AI领域的创新路径与实践价值

本文来源atlassian.com&#xff0c;由Atlassian全球白金合作伙伴——龙智翻译整理。 二十余年来&#xff0c;Atlassian始终是创新领域的领军者。凭借对团队协作本质的深刻理解&#xff0c;Atlassian在AI时代仍持续引领协作方式的革新。如今&#xff0c;这一领先地位再次获得权威…...

Kafka 安装教程(支持 Windows / Linux / macOS)

一、下载 1、kafka官网下载地址:https://kafka.apache.org/downloads 根据实际情况下载对应的版本 2、JDK的版本最好是17+ JDK下载地址:https://www.oracle.com/java/technologies/javase/jdk17-0-13-later-archive-downloads.html 二、安装 前置条件 安装 Java(至少 Jav…...

OpenCV种的cv::Mat与Qt种的QImage类型相互转换

一、首先了解cv::Mat结构体 cv::Mat::step与QImage转换有着较大的关系。 step的几个类别区分: step:矩阵第一行元素的字节数step[0]:矩阵第一行元素的字节数step[1]:矩阵中一个元素的字节数step1(0):矩阵中一行有几个通道数step1(1):一个元素有几个通道数(channel()) cv::Ma…...

机器学习——什么时候使用决策树

无论是决策树&#xff0c;包括集成树还是神经网络都是非常强大、有效的学习方法。 下面是各自的优缺点&#xff1a; 决策树和集成树通常在表格数据上表现良好&#xff0c;也称为结构化数据&#xff0c;这意味着如果你的数据集看起来像一个巨大的电子表格&#xff0c;那么决策…...

llm-d:面向Kubernetes的高性能分布式LLM推理框架

在生成式AI&#xff08;GenAI&#xff09;浪潮中&#xff0c;高效、经济地部署和扩展大型语言模型&#xff08;LLM&#xff09;推理服务是企业面临的核心挑战。传统基于Kubernetes的横向扩展&#xff08;Scale-out&#xff09;和负载均衡策略在处理独特的LLM推理工作负载时往往…...

前端没有“秦始皇“,但可以做跨端的王[特殊字符]

前端各领域的 “百家争鸣” 框架之争&#xff1a;有 React、Vue、Angular 等多种框架。它们各有优缺点&#xff0c;开发者之间还存在鄙视链&#xff0c;比如 Vue 嫌 React 难用&#xff0c;React 嫌 Vue 不够灵活。样式处理&#xff1a; CSS 预处理器&#xff1a;像 Sass、Les…...

Flutter如何支持原生View

在 Flutter 中集成原生 View&#xff08;如 Android 的 SurfaceView、iOS 的 WKWebView&#xff09;是通过 平台视图&#xff08;Platform View&#xff09; 实现的。这一机制允许在 Flutter UI 中嵌入原生组件&#xff0c;解决了某些场景下 Flutter 自身渲染能力的不足&#x…...

mongodb源码分析session异步接受asyncSourceMessage()客户端流变Message对象

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制&#xff0c;ASIOSession和connection是循环接受客户端命令&#xff0c;状态转变流程是&#xff1a;State::Created 》 State::Source 》State::…...

【数据分析】什么是鲁棒性?

引言 —— 为什么我们需要“抗折腾”的系统&#xff1f; 当你乘坐的飞机穿越雷暴区时机体剧烈颠簸&#xff0c;自动驾驶汽车在暴雨中稳稳避开障碍物&#xff0c;或是手机从口袋摔落后依然流畅运行——这些场景背后&#xff0c;都藏着一个工程领域的“隐形守护者”&#xff1a;…...

适老化场景重构:现代家政老年照护虚拟仿真实训室建设方案​

随着老龄化社会的深度发展&#xff0c;老年照护服务的专业化需求对人才培养提出了更高要求。 凯禾瑞华以现代家政管理理念为核心&#xff0c;推出老年照护虚拟仿真实训室建设方案&#xff0c;通过虚拟仿真技术重构适老化生活场景&#xff0c;融合数字课程、产教融合及搭载智能…...

Qt/C++学习系列之QGroupBox控件的简单使用

Qt/C学习系列之QGroupBox控件的简单使用 前言样式使用代码层面初始化控件事件过滤器点击事件处理 总结 前言 最近在练手一个项目&#xff0c;项目中有不同功能的划分&#xff0c;为了功能分区一目了然&#xff0c;我使用到QGroupBox控件&#xff0c;也是在界面排版布局中最常用…...

Ubuntu设置之初始化

安装SSH服务 # 安装 OpenSSH Server sudo apt update sudo apt install -y openssh-server# 检查 SSH 服务状态 sudo systemctl status ssh # Active: active (running) since Sat 2025-05-31 17:13:07 CST; 6s ago# 重启服务 sudo systemctl restart ssh自定义分辨率 新…...

如何轻松地将数据从 iPhone传输到iPhone 16

对升级到 iPhone 16 感到兴奋吗&#xff1f;恭喜&#xff01;然而&#xff0c;除了兴奋之外&#xff0c;学习如何将数据从 iPhone 传输到 iPhone 16 也很重要。毕竟&#xff0c;那些重要的联系人、笔记等都是不可或缺的。为了实现轻松的iPhone 到 iPhone 传输&#xff0c;我们总…...

开源供应链攻击持续发酵,多个软件包仓库惊现恶意组件

近期在npm、Python和Ruby软件包仓库中相继发现多组恶意组件&#xff0c;这些组件能够清空加密货币钱包资金、安装后删除整个代码库并窃取Telegram API令牌&#xff0c;再次印证了开源生态系统中潜伏的多样化供应链威胁。 多平台恶意组件集中曝光 Checkmarx、ReversingLabs、S…...

Docker Compose 备忘

1。docker-compose.yml services:air-web:build: .ports:- "1027:1027"volumes:- .:/codedepends_on:- air-redisair-redis:image: "redis:alpine" 2. DockerfileFROM python:3.12-slim-bookworm #设置工作目录 WORKDIR /code #将当前目录内容拷贝到容器…...

量子计算+AI:特征选择与神经网络优化创新应用

在由玻色量子协办的第二届APMCM“五岳杯”量子计算挑战赛中&#xff0c;来自北京理工大学的Q-Masterminds团队摘取了银奖。该团队由北京理工大学张玉利教授指导&#xff0c;依托玻色量子550计算量子比特的相干光量子计算机&#xff0c;将量子计算技术集成到特征选择和神经网络剪…...

算法分析与设计-动态规划、贪心算法

目录 第三章——动态规划 第四章——贪心算法 第三章——动态规划 /*【问题描述】 使用动态规划算法解矩阵连乘问题&#xff0c;具体来说就是&#xff0c;依据其递归式自底向上的方式进行计算&#xff0c;在计算过程中&#xff0c;保存子问题答案&#xff0c;每个子问题只解…...

光伏功率预测新突破:TCN-ECANet-GRU混合模型详解与复现

研究背景 ​背景与挑战​ 光伏发电受天气非线性影响,传统方法(统计模型、机器学习)难以处理高维时序数据,预测误差大。​创新模型提出​ 融合时序卷积网络(TCN)、高效通道注意力(ECANet)和门控循环单元(GRU)的混合架构。​方法论细节​ TCN:膨胀因果卷积提取长时序特…...

React组件基础

组件是什么&#xff1f; 概念&#xff1a;一个组件就是用户界面的一部分&#xff0c;它可以有自己的逻辑和外观&#xff0c;组件之间可以相互嵌套&#xff0c;也可以多次复用 组件化开发&#xff0c;可以让开发者像搭积木一样构建一个完整庞大的应用 react组件 在react中&a…...

2025年5月24日系统架构设计师考试题目回顾

当前仅仅是个人用于记录&#xff0c;还未做详细分析&#xff0c;待更新… 综合知识 设 x,y 满足约束条件&#xff1a;x-1>0, x-y<0, x-y-x<0, 则 y/x 的最大值是()。 A. 3 B. 2 C. 4 D. 1 申请软件著作权登记时应当向中国版本保护中心提交软件的鉴别材料&#xff…...

ABP 框架集成 EasyAbp.Abp.GraphQL 构建高性能 GraphQL API

&#x1f680; ABP 框架集成 EasyAbp.Abp.GraphQL 构建高性能 GraphQL API &#x1f4da; 目录 &#x1f680; ABP 框架集成 EasyAbp.Abp.GraphQL 构建高性能 GraphQL API&#x1f9ed; 背景与目标&#x1f6e0; 安装与依赖&#x1f4e6; 模块注册与启动MyProjectHttpApiHostMo…...

C# 用户控件(User Control)详解:创建、使用与最佳实践

在C#应用程序开发中&#xff0c;用户控件&#xff08;User Control&#xff09;是一种强大的工具&#xff0c;它允许开发者将多个标准控件组合成一个可复用的自定义组件。无论是Windows Forms还是WPF&#xff0c;用户控件都能显著提高UI开发的效率&#xff0c;减少重复代码&…...