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

双指针:不止是 O(n²) 降 O(n),更是换个角度看问题

一句话理解:双指针就是给数组安排两个"探针",一个从前、一个从后,或者一快一慢,把需要两层循环才能解决的问题用一趟扫描搞定。

🎯 本文产出

  • 双指针通用代码模板(Python/Java,可直接复制)
  • 3 道面试真题(从 Easy 到 Hard)
  • 链表环检测、三数之和等真实场景
  • 面试官的评分标准和踩坑清单

❓ 这个模式解决什么问题

刷过 LeetCode 的人都知道:看到数组、有序、求两数、判环这类描述,脑子里第一反应就是双指针。

但双指针的核心不是"用两个变量遍历数组"——它在降低问题维度

暴力解是用两层循环穷举所有组合。双指针告诉你:有些组合根本不需要检查。有序数组或者单调性约束天然帮你剪掉了不可能的解空间。

不是扫描得更快,是更聪明地决定扫什么。

双指针的三大家族

变体典型步态时间复杂度经典应用
对撞指针left++ / right–O(n)两数之和 II、盛最多水的容器
快慢指针fast 走 2 步 / slow 走 1 步O(n)链表环检测、找中点
同向指针两个指针都在移动但不回头O(n)去重、移除元素、滑动窗口

🧩 模式识别:什么样的题用这个模式

特征说明例题
有序数组 + 找一对元素排序后利用单调性,一个大了改小、小了改大两数之和 II(167)
需要 O(1) 额外空间不允许用哈希表,只允许在原数组上操作验证回文串(125)
子数组/子串的连续判断两个指针框出一个区间做检查无重复字符的最长子串(3)
链表相关问题环、交点、中点、倒数第 k 个环形链表(141)
原地修改数组需要 O(1) 空间 + O(n) 时间排除重复/删除元素移除元素(27)
三个以上元素的组合固定一个 + 双指针缩小搜索范围三数之和(15)

一句话判断:如果你想到了暴力 O(n²),但数据规模让你不敢下手——试试能不能先排序,然后用双指针降到 O(n)


💻 代码模板

对撞指针(最常用)

deftwo_pointers(arr,target):""" 对撞指针:数组有序时使用 """left,right=0,len(arr)-1whileleft<right:current=arr[left]+arr[right]# 或其他判断条件ifcurrent==target:return[left,right]# 找到目标elifcurrent<target:left+=1# 值太小,左指针右移else:right-=1# 值太大,右指针左移return[]# 未找到
// Java 版:对撞指针publicint[]twoPointers(int[]nums,inttarget){intleft=0,right=nums.length-1;while(left<right){intsum=nums[left]+nums[right];if(sum==target){returnnewint[]{left,right};}elseif(sum<target){left++;}else{right--;}}returnnewint[0];}

快慢指针

defhas_cycle(head):"""快慢指针检测链表环"""slow=fast=headwhilefastandfast.next:slow=slow.next# 慢指针走 1 步fast=fast.next.next# 快指针走 2 步ifslow==fast:# 相遇则说明有环returnTruereturnFalse# 快指针走到结尾 → 无环

🎬 真实产品场景:Instagram 互关检测——你和另一个用户如果互相关注,在社交图谱中形成一个双向边。但如果你关注了 A、A 关注了 B、B 又关注了你,这就形成了环。快慢指针可以在 O(n) 时间内检测社交图中是否存在这种循环依赖,比哈希表方案更省空间。

同向指针(去重/原地修改)

defremove_duplicates(nums):"""原地去重,返回新长度"""ifnotnums:return0write=1# 写入指针,从第二个位置开始forreadinrange(1,len(nums)):# 读取指针遍历ifnums[read]!=nums[write-1]:nums[write]=nums[read]write+=1returnwrite

🏗️ 核心原理

双指针为什么能降低复杂度?看这张图就明白了:

否,但连续子数组

链表问题

两层暴力循环
O(n²)

数组是否有序
或存在单调性?

对撞指针
O(n)

同向/快慢指针
O(n)

快慢指针
O(n)

理解:每次移动排除一行搜索空间

理解:窗口滑动代替嵌套

理解:异速追及 = 环检测

每次只排除一对

双指针法

大了

小了

left = 0, right = n-1

sum = target?

right--

left++

暴力法

i: 0 → n

j: i+1 → n

≈ n²/2 次比较

在有序数组中用对撞指针,每次 left++ 或 right-- 不是"排除一个元素",而是排除了所有包含这个元素的组合

  • 暴力:检查 C(n,2) = n(n-1)/2 对 → O(n²)
  • 双指针:每次移动排除一整行/列 → O(n)

二维搜索降为一维搜索,就这么回事。


🎯 三题进阶

第 1 题(Easy):验证回文串

题目:给定一个字符串,只考虑字母和数字,忽略大小写,判断它是否是回文。

为什么这是双指针:回文的定义就是"从前往后读"等于"从后往前读"——天然的对撞指针。

defis_palindrome(s:str)->bool:left,right=0,len(s)-1whileleft<right:# 跳过非字母数字字符whileleft<rightandnots[left].isalnum():left+=1whileleft<rightandnots[right].isalnum():right-=1ifs[left].lower()!=s[right].lower():returnFalseleft+=1right-=1returnTrue

模式识别:看到"字符串首尾对比"+"忽略某些字符"→ 对撞指针跳过非法字符。


第 2 题(Medium):三数之和

题目:给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j != k 且 nums[i] + nums[j] + nums[k] == 0。返回所有不重复的三元组。

为什么这是双指针:固定一个数后,问题转化为"两数之和"→ 对撞指针。

defthree_sum(nums):nums.sort()# 重点是先排序res=[]foriinrange(len(nums)-2):# 去重:跳过重复的固定元素ifi>0andnums[i]==nums[i-1]:continueleft,right=i+1,len(nums)-1whileleft<right:total=nums[i]+nums[left]+nums[right]iftotal==0:res.append([nums[i],nums[left],nums[right]])# 去重:跳过相同取值whileleft<rightandnums[left]==nums[left+1]:left+=1whileleft<rightandnums[right]==nums[right-1]:right-=1left+=1right-=1eliftotal<0:left+=1else:right-=1returnres

🎬 真实产品场景:Netflix 个性化推荐——你有 3 个推荐算法,每个输出一个"热门分数",你想找出 3 个算法之和为 0 的配置(即平衡覆盖冷热内容)。三数之和正是这个问题的抽象。

复杂度:排序 O(n log n) + 双指针 O(n²) = O(n²),比暴力 O(n³) 快一个数量级。


第 3 题(Hard):盛最多水的容器

题目:给定 n 个非负整数 a₁, a₂, …, aₙ,每个数代表坐标 (i, aᵢ),找两条线使得它们与 x 轴共同构成的容器能装最多水。

盛水问题图示

左矮

右矮

height
| | | |

left=0, right=n-1

哪个板矮?

面积=left_h × (right-left)
left++

面积=right_h × (right-left)
right--

更新最大值

defmax_area(height):left,right=0,len(height)-1max_water=0whileleft<right:# 面积取决于矮的那条线h=min(height[left],height[right])w=right-left max_water=max(max_water,h*w)# 移动矮的那端——因为只有移动矮的才可能变大ifheight[left]<height[right]:left+=1else:right-=1returnmax_water

🎬 真实产品场景:AWS 的流量调度——给定每个数据中心的带宽容量,找出两个数据中心之间能流过的最大流量。容量较少的那个数据中心是瓶颈,这就和"盛水"问题一样:能装多少水取决于较短的板。

为什么暴力解 O(n²) 不行:n = 10⁵ 时,暴力需要 5×10⁹ 次操作,约 50 秒。双指针 O(n) 只需 10⁵ 次操作,0.01 秒。


⚡ 面试考点

考察重点

级别要求加分项
Easy写出双指针基本逻辑能处理边界(空数组/单元素)
Medium正确用双指针降低复杂度能讲清楚"为什么可以这样优化"
Hard在复杂约束中灵活变通能分析单调性来源,证明指针移动的正确性

面试官在意的三个问题

  1. “为什么指针向这个方向移动?”——你要能用单调性证明:移动另一个方向一定得不到更优解(盛水问题:移动长板面积一定不变或减少)。

  2. “时间复杂度是多少?”——必须能准确说出 O(n) 或 O(n log n),并解释为什么不是 O(n²)。

  3. “能处理什么边界情况?”——空数组、一个元素、所有元素相同、已经有序/乱序。

常见踩坑清单

踩坑点❌ 错误写法✅ 正确写法
死循环while left <= right不移动指针while left < right+ 保证每次至少移动一个指针
去重丢失发现合法解后忘记跳过重复值双指针去重:while left < right and nums[left] == nums[left+1]
指针移动方向反了if sum < target: right--有序数组中,和小了应当 left++(增大)
快慢指针初始化slow = head; fast = head;但没检查空链表if not head: return False
同向指针乱序写入指针边界没处理好write初始化为 1(第一个元素默认保留)

📝 作业

为了真正掌握双指针,看完文章后自己动手写这几道题:

题目难度双指针类型预计时间
167. 两数之和 II - 输入有序数组Easy对撞指针10 min
283. 移动零Easy同向指针10 min
15. 三数之和Medium排序 + 对撞20 min
142. 环形链表 IIMedium快慢指针15 min
11. 盛最多水的容器Medium对撞指针15 min
42. 接雨水Hard对撞指针30 min

信息来源说明

  • ✅ 已验证:LeetCode 官方题解 + 作者实测(Python 3.12, 2026)
  • ✅ 已验证:面试场景基于 2024-2026 年多家公司面经数据
  • 📄 参考:LeetCode 题解讨论区(Two Pointers tag)

下一篇预告:滑动窗口——连续子数组问题的终极解法

http://www.rkmt.cn/news/1427640.html

相关文章:

  • 基于树莓派的智能调酒机:从物联网架构到软硬件全栈实践
  • 告别手动拖拽!用Unity编辑器扩展一键搞定Substance Painter贴图与材质匹配
  • 基于Teensy 4.1与步进电机的全自动魔方求解器设计与实现
  • 江西30米ASTER GDEM V3高程数据包(含WGS84坐标系与省级边界矢量)
  • DLSS Swapper完全指南:免费开源的游戏DLSS文件管理终极方案
  • ELF技术:机器学习加速逻辑综合的工程实践
  • 免费歌词制作神器:5分钟掌握专业级LRC歌词同步技巧
  • 3个思维转变:用AlienFX Tools将你的Alienware从工具变为伙伴
  • 基于TM4C123GH6PM的西蒙游戏实现:从寄存器操作到嵌入式系统设计
  • 颠覆传统:Seraphine智能助手如何用3大核心功能重塑你的英雄联盟游戏体验
  • Redis 数据类型命令详解
  • ChatGPT如何解答奇葩谜题:从原理到实践的全方位解析
  • AMD Ryzen SMU调试工具实战指南:深度优化CPU性能的5个核心场景
  • OpenClaw代码注释自动生成与优化:适配企业规范,告别手动写注释
  • 3步完成CPU单核稳定性测试:CoreCycler终极指南
  • COM3D2.MaidFiddler:免费实时角色编辑器终极指南 [特殊字符]
  • WechatDecrypt微信消息解密完整指南:三步解锁你的聊天记录
  • KMS智能激活脚本:3分钟永久激活Windows与Office的终极指南
  • 猫抓Cat-Catch技术架构解析与实战指南:浏览器资源嗅探的现代解决方案
  • 从技术布道到行业偶像:解析山姆·奥特曼的AI领导力与OpenAI崛起
  • 论文查重真的有那么可怕吗?用书匠策AI免费查重,三分钟搞懂全流程
  • 阴阳师自动化脚本:3步解放双手,智能完成日常任务
  • 保姆级教程:在Linux服务器上配置PCIe AER,让你的系统错误无处遁形
  • 基于STM32与LoRa的20路继电器远程监控系统设计与实现
  • Agent 一接权限申请单就开始提错审批人:从 Approver Scope 到 Submit Proof 的工程实战
  • 别再纠结CSR和SSR了!用Node.js + jsdom手把手教你模拟浏览器渲染,5分钟搞懂服务端生成HTML
  • 从Arduino UNO到RP2350:硬件迁移、代码优化与性能提升实战
  • 【Lovable云平台搭建终极指南】:20年架构师亲授从零到高可用的7大核心步骤
  • 绝了!原来毕业论文有这操作?2026降AIGC网站推荐合集
  • 别再收藏杂七杂八的链接了!一个网站搞定开发调试所有需求