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

【Java实习面试算法冲刺】双指针

第2类题型:双指针

为什么双指针题看起来不难,你一到面试就容易写乱

很多同学第一次刷双指针时,会觉得这类题比哈希表还“直观”。因为代码通常不长,变量也常常只有leftrightslowfast四个名字。但真正到了面试现场,双指针反而很容易暴露出两类问题:

  • 你会套模板,但说不清两个指针各自代表什么。
  • 你知道要移动某一边,却解释不出“为什么这样移动不会漏解”。
  • 你能把三数之和写个大概,却总在去重和边界上翻车。
  • 你把“会写代码”当成“理解题型”,结果一换题面就不稳。

如果你现在正处在“Easy 基本能做,Medium 一追问就虚”的阶段,双指针是非常值得单独攻克的一类题。读完这篇,你至少要把 3 件事练熟:先判断是快慢指针还是左右指针、能把代表题写稳、能在面试里解释每一步为什么这么移动。


文章目录

  • 第2类题型:双指针
    • 1. 核心知识点
      • 动画辅助理解:固定一个数,再让左右指针夹逼
    • 2. 这类题在面试里考什么
    • 3. 高频题清单
    • 4. 这类题最容易犯的 3 个错误
    • 5. 代表题精讲 1
      • 题目
      • 思路
      • Java 代码
      • 手推一遍最容易记住的状态变化
      • 复杂度
      • 如果这是面试现场,你可以这样说
    • 6. 代表题精讲 2
      • 题目
      • 思路
      • Java 代码
      • 手推一遍最能看懂去重和移动逻辑
      • 复杂度
      • 如果这是面试现场,你可以这样说
    • 7. 其余题模板与关键片段
      • [`26. 删除有序数组中的重复项`](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/)
      • [`11. 盛最多水的容器`](https://leetcode.cn/problems/container-with-most-water/)
    • 8. 边界、易混点与替代方案
      • 快慢指针最容易错在哪
      • 左右指针最容易错在哪
      • `三数之和` 为什么总有人写错
      • 这类题怎么判断值不值得用双指针
    • 9. 你学完后怎么验证自己真的会了
    • 10. 错题本记录方式
    • 11. 适用范围与边界
    • 12. 面试前 3 分钟速记
    • 13. 结尾:把“会套模板”变成“会判断、会证明、会自测”

1. 核心知识点

双指针本质上有两类模型:

  • 快慢指针:常用于原地覆盖、去重、移动元素。
  • 左右指针:常用于有序数组、逼近目标值、利用单调性缩小范围。

识别信号很常见:

  • 数组要原地修改。
  • 已排序,或者可以先排序。
  • 要找两边夹逼、最优面积、和问题。
  • 存在去重细节。

最基础的快慢指针模板是:

intslow=0;for(intfast=0;fast<nums.length;fast++){if(满足保留条件){nums[slow]=nums[fast];slow++;}}

左右指针模板则更像:

intleft=0;intright=nums.length-1;while(left<right){// 根据条件移动 left 或 right}

真正决定你能不能写对的,不是模板本身,而是你能不能说清楚两个指针各自代表什么,以及每一步移动为什么不会漏答案。

动画辅助理解:固定一个数,再让左右指针夹逼

双指针最适合用15. 三数之和建立第一印象。你可以先打开本地动画页:

双指针:三数之和分步动画

这个动画最值得观察的是两件事:

  • 外层先固定一个数nums[i]
  • 内层用leftright在剩余区间里根据和的大小夹逼。

2. 这类题在面试里考什么

双指针题看起来不难,但特别能暴露“会背模板”和“真正理解不变量”的差距。

面试官通常在看:

  • 你是否能解释两个指针的含义。
  • 你为什么移动左边而不是右边。
  • 你如何证明不会漏掉最优解。
  • 你是否处理好了去重和边界。

很多候选人会写出一个大概对的结构,但一旦被追问“为什么这一步能这么移”,就说不清楚。这正是双指针题的区分度。


3. 高频题清单

题目来源难度高频属性
283. 移动零LeetCode 热题 100Easy面试高频
26. 删除有序数组中的重复项面试经典 150Easy基础高频
11. 盛最多水的容器LeetCode 热题 100Medium高频 Medium
15. 三数之和LeetCode 热题 100Medium真实面经高频

4. 这类题最容易犯的 3 个错误

  1. 双指针能做的题,还在用多重循环硬枚举。
  2. 左右指针会移动,但说不清为什么这么移动不会漏解。
  3. 三数之和这类题排序之后,去重细节没有写全。

5. 代表题精讲 1

题目

283. 移动零

思路

这题是最典型的快慢指针。要求把所有0移到数组末尾,同时保持非零元素的相对顺序。

可以把slow理解为“下一个非零元素应该放的位置”,把fast理解为“当前正在扫描的位置”。

做法:

  1. fast从左到右扫描。
  2. 如果nums[fast] != 0,就把它放到nums[slow],然后slow++
  3. 第一轮结束后,[0, slow - 1]已经是所有非零元素。
  4. 再把[slow, n - 1]全部填成0

Java 代码

classSolution{publicvoidmoveZeroes(int[]nums){intslow=0;for(intfast=0;fast<nums.length;fast++){if(nums[fast]!=0){nums[slow]=nums[fast];slow++;}}while(slow<nums.length){nums[slow]=0;slow++;}}}

手推一遍最容易记住的状态变化

nums = [0, 1, 0, 3, 12]为例:

fast位置当前值slow位置含义操作数组变化
00下一个非零该放的位置跳过[0, 1, 0, 3, 12]
1101放到nums[0]slow++[1, 1, 0, 3, 12]
201跳过[1, 1, 0, 3, 12]
3313放到nums[1]slow++[1, 3, 0, 3, 12]
412212放到nums[2]slow++[1, 3, 12, 3, 12]

第一轮结束后,前 3 个位置已经是正确的非零序列[1, 3, 12],所以只需要把后面补成0,最终得到[1, 3, 12, 0, 0]

这个过程最关键的不是“把 0 移到后面”,而是始终维护一个不变量:[0, slow - 1]永远是已经处理好的非零区间。

复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

如果这是面试现场,你可以这样说

这题我会把它归类为快慢指针。fast负责扫描数组,slow指向下一个应该放非零元素的位置。扫描到非零元素时就覆盖到slow位置,最后把剩余位置补零。这样能在线性时间、常数空间内完成原地修改,而且能保持相对顺序不变。


6. 代表题精讲 2

题目

15. 三数之和

思路

这题是双指针里非常有代表性的 Medium。暴力做法是三重循环,复杂度O(n^3),明显过高。

优化思路分三步:

  1. 先排序。
  2. 固定第一个数nums[i]
  3. 在剩余区间里用左右指针找两数之和等于-nums[i]

排序之后,左右指针可以利用和的大小移动:

  • 如果和太小,左指针右移。
  • 如果和太大,右指针左移。
  • 如果刚好相等,记录答案后两边都跳过重复值。

去重是这题真正的难点:

  • i要去重。
  • 命中答案后,left要跳过重复值。
  • 命中答案后,right也要跳过重复值。

Java 代码

importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;classSolution{publicList<List<Integer>>threeSum(int[]nums){Arrays.sort(nums);List<List<Integer>>result=newArrayList<>();for(inti=0;i<nums.length-2;i++){if(i>0&&nums[i]==nums[i-1]){continue;}intleft=i+1;intright=nums.length-1;while(left<right){intsum=nums[i]+nums[left]+nums[right];if(sum<0){left++;}elseif(sum>0){right--;}else{result.add(Arrays.asList(nums[i],nums[left],nums[right]));left++;right--;while(left<right&&nums[left]==nums[left-1]){left++;}while(left<right&&nums[right]==nums[right+1]){right--;}}}}returnresult;}}

手推一遍最能看懂去重和移动逻辑

nums = [-1, 0, 1, 2, -1, -4]为例,先排序后得到:

[-4, -1, -1, 0, 1, 2]

第一轮固定i = 0,也就是nums[i] = -4

  • left = 1right = 5,和为-4 + (-1) + 2 = -3
  • 和太小,说明要变大,只能让left++
  • 后续无论怎么夹逼,这轮都找不到答案

第二轮固定i = 1,也就是nums[i] = -1

  • left = 2right = 5,和为-1 + (-1) + 2 = 0
  • 命中一个答案[-1, -1, 2]
  • 然后left++right--,继续找这一轮里别的答案

接着:

  • left = 3right = 4,和为-1 + 0 + 1 = 0
  • 再命中一个答案[-1, 0, 1]

再往后leftright相遇,结束这一轮。

第三轮如果i = 2,此时nums[2]还是-1,和前一个固定值重复,所以必须直接跳过,否则会重复得到同样答案。

这段手推最值得你记住的是 3 个动作:

  • 排序后再做夹逼。
  • 和太小就移动左边,和太大就移动右边。
  • 命中答案后,不只要收缩两边,还要跳过重复值。

复杂度

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1),不计答案空间

如果这是面试现场,你可以这样说

这题我会先排序,然后把它转成“固定一个数,在剩余区间里做两数之和”的问题。排序后可以用左右指针根据和的大小移动,并且方便去重。整体复杂度从暴力的O(n^3)降到O(n^2)。这题最关键的不是双指针本身,而是排序后的去重逻辑要完整。

7. 其余题模板与关键片段

26. 删除有序数组中的重复项

快慢指针维护“已去重区间”的结尾:

intslow=1;for(intfast=1;fast<nums.length;fast++){if(nums[fast]!=nums[fast-1]){nums[slow]=nums[fast];slow++;}}returnslow;

11. 盛最多水的容器

核心不是枚举每一对,而是用左右指针逼近:

intleft=0;intright=height.length-1;intbest=0;while(left<right){intarea=(right-left)*Math.min(height[left],height[right]);best=Math.max(best,area);if(height[left]<height[right]){left++;}else{right--;}}

为什么移动较短板,是这题必须会讲的点。因为当前面积由短板决定,如果你保留较短板不动,只缩小宽度,那么面积上界只会更差;只有尝试换掉短板,才有机会让最小高度变高。


8. 边界、易混点与替代方案

快慢指针最容易错在哪

左右指针最容易错在哪

三数之和为什么总有人写错

这类题怎么判断值不值得用双指针


9. 你学完后怎么验证自己真的会了

不要只看懂代码。更有效的做法,是用下面 3 组自测判断你是否真的掌握:

  1. 10 分钟内独立写出移动零,并能解释slow为什么始终指向“下一个非零该放的位置”。
  2. 看到三数之和时,能主动说出“先排序、固定一个数、左右夹逼、三层去重”。
  3. 看到盛最多水的容器时,能解释为什么移动短板不会漏掉最优解。

如果你在自测时出现下面任意一种情况,就说明还没有真正掌握:

比较稳妥的过关标准是:你能在 15 分钟内做出一道基础双指针题,并且在 1 分钟内口述题型判断、指针含义、移动逻辑和复杂度。

10. 错题本记录方式

双指针题错题本重点记录:

11. 适用范围与边界

如果你当前连“排序后为什么能用左右夹逼”都说不清,建议先拿两数之和 II移动零盛最多水的容器这几题把基本模型练顺,再去刷更复杂的变形题。


12. 面试前 3 分钟速记

13. 结尾:把“会套模板”变成“会判断、会证明、会自测”

双指针之所以适合作为第二类高频题型,不是因为它代码短,而是因为它特别能训练你的题型判断力。你真正要练成的,不是背下两个模板,而是形成几个稳定动作:

如果这几个动作稳定下来,后面的滑动窗口、链表、二分和回溯,你都会更容易进入状态。


感谢阅读,记得点赞、关注、收藏,欢迎各位评论区交流!!!

下一期:疯狂码字ing……

http://www.jsqmd.com/news/1126039/

相关文章:

  • 小米穿戴设备表盘设计终极指南:3分钟掌握可视化表盘设计
  • 美团 Leaf-snowflake 分布式 ID 生成器 k8s 改造的想法
  • 一键备份你的QQ空间回忆:GetQzonehistory完整备份指南
  • 3个关键步骤彻底掌握Pyfa:EVE玩家的离线配装革命
  • GEO代理可以独立运营品牌吗
  • 【Hermes入门11讲】第七讲:定时自动化——让Hermes成为你的24小时助手
  • AI 英语学习软件开发流程
  • 差压式孔板流量计选型要点
  • 5个步骤搭建免费动作捕捉系统:FreeMoCap完全指南
  • Azure Local 离线模式AKS Arc 管理(系列篇十三)
  • Kafka不是消息队列:事件流架构的核心原理与工程实践
  • 成都茶台定制哪家好
  • GetQzonehistory:5分钟快速导出QQ空间历史说说完整指南
  • OpenCloudOS 9 - Cube Sandbox技术交流会
  • 3个高效文件同步场景解析:ChoEazyCopy实战应用指南
  • 直流电机静音控制技术与TB9051FTG应用解析
  • DBeaver驱动包:一站式解决数据库连接配置难题
  • PyTorch 1.13 BCEWithLogitsLoss 实战:3 个代码示例解析数值稳定性优势
  • ViT工业落地实战:解决CNN失效区的视觉任务瓶颈
  • 自己动手开发编译器(九)CPS风格的解析器组合子
  • 抖店多店订单怎么区分采购店群商家如何避免订单混乱
  • 163、调试手记:虚拟机里PCIE设备怎么“丢”了?
  • 美国签证预约智能监控工具:5步实现自动抢号的高效解决方案
  • 国内网络变压器领域已有多家厂商在特定技术指标、可靠性及量产一致性上达到甚至超越普思(Pulse Electronics)和伯恩斯(Bourns)的水平,尤其在工业级宽温、PoE供电稳定性、高速信号完整
  • 成都智能靠谱之处大揭秘
  • 深度揭秘MapLibre:当开源地图遇上无限可能
  • 八股文:计算机网络
  • 首先要说明的是连接数是有限制的:
  • 打破开题写作内耗:okbiye 一站式 AI 开题报告工具,高效打通论文起步全链路
  • 微信 API 实战:客户标签体系设计与自动打标系统开发