第2类题型双指针为什么双指针题看起来不难你一到面试就容易写乱很多同学第一次刷双指针时会觉得这类题比哈希表还“直观”。因为代码通常不长变量也常常只有left、right、slow、fast四个名字。但真正到了面试现场双指针反而很容易暴露出两类问题你会套模板但说不清两个指针各自代表什么。你知道要移动某一边却解释不出“为什么这样移动不会漏解”。你能把三数之和写个大概却总在去重和边界上翻车。你把“会写代码”当成“理解题型”结果一换题面就不稳。如果你现在正处在“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. 核心知识点双指针本质上有两类模型快慢指针常用于原地覆盖、去重、移动元素。左右指针常用于有序数组、逼近目标值、利用单调性缩小范围。识别信号很常见数组要原地修改。已排序或者可以先排序。要找两边夹逼、最优面积、和问题。存在去重细节。最基础的快慢指针模板是intslow0;for(intfast0;fastnums.length;fast){if(满足保留条件){nums[slow]nums[fast];slow;}}左右指针模板则更像intleft0;intrightnums.length-1;while(leftright){// 根据条件移动 left 或 right}真正决定你能不能写对的不是模板本身而是你能不能说清楚两个指针各自代表什么以及每一步移动为什么不会漏答案。动画辅助理解固定一个数再让左右指针夹逼双指针最适合用15. 三数之和建立第一印象。你可以先打开本地动画页双指针三数之和分步动画这个动画最值得观察的是两件事外层先固定一个数nums[i]。内层用left和right在剩余区间里根据和的大小夹逼。2. 这类题在面试里考什么双指针题看起来不难但特别能暴露“会背模板”和“真正理解不变量”的差距。面试官通常在看你是否能解释两个指针的含义。你为什么移动左边而不是右边。你如何证明不会漏掉最优解。你是否处理好了去重和边界。很多候选人会写出一个大概对的结构但一旦被追问“为什么这一步能这么移”就说不清楚。这正是双指针题的区分度。3. 高频题清单题目来源难度高频属性283. 移动零LeetCode 热题 100Easy面试高频26. 删除有序数组中的重复项面试经典 150Easy基础高频11. 盛最多水的容器LeetCode 热题 100Medium高频 Medium15. 三数之和LeetCode 热题 100Medium真实面经高频4. 这类题最容易犯的 3 个错误双指针能做的题还在用多重循环硬枚举。左右指针会移动但说不清为什么这么移动不会漏解。三数之和这类题排序之后去重细节没有写全。5. 代表题精讲 1题目283. 移动零思路这题是最典型的快慢指针。要求把所有0移到数组末尾同时保持非零元素的相对顺序。可以把slow理解为“下一个非零元素应该放的位置”把fast理解为“当前正在扫描的位置”。做法用fast从左到右扫描。如果nums[fast] ! 0就把它放到nums[slow]然后slow。第一轮结束后[0, slow - 1]已经是所有非零元素。再把[slow, n - 1]全部填成0。Java 代码classSolution{publicvoidmoveZeroes(int[]nums){intslow0;for(intfast0;fastnums.length;fast){if(nums[fast]!0){nums[slow]nums[fast];slow;}}while(slownums.length){nums[slow]0;slow;}}}手推一遍最容易记住的状态变化以nums [0, 1, 0, 3, 12]为例fast位置当前值slow位置含义操作数组变化00下一个非零该放的位置跳过[0, 1, 0, 3, 12]110把1放到nums[0]slow[1, 1, 0, 3, 12]201跳过[1, 1, 0, 3, 12]331把3放到nums[1]slow[1, 3, 0, 3, 12]4122把12放到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)明显过高。优化思路分三步先排序。固定第一个数nums[i]。在剩余区间里用左右指针找两数之和等于-nums[i]。排序之后左右指针可以利用和的大小移动如果和太小左指针右移。如果和太大右指针左移。如果刚好相等记录答案后两边都跳过重复值。去重是这题真正的难点i要去重。命中答案后left要跳过重复值。命中答案后right也要跳过重复值。Java 代码importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;classSolution{publicListListIntegerthreeSum(int[]nums){Arrays.sort(nums);ListListIntegerresultnewArrayList();for(inti0;inums.length-2;i){if(i0nums[i]nums[i-1]){continue;}intlefti1;intrightnums.length-1;while(leftright){intsumnums[i]nums[left]nums[right];if(sum0){left;}elseif(sum0){right--;}else{result.add(Arrays.asList(nums[i],nums[left],nums[right]));left;right--;while(leftrightnums[left]nums[left-1]){left;}while(leftrightnums[right]nums[right1]){right--;}}}}returnresult;}}手推一遍最能看懂去重和移动逻辑以nums [-1, 0, 1, 2, -1, -4]为例先排序后得到[-4, -1, -1, 0, 1, 2]第一轮固定i 0也就是nums[i] -4left 1right 5和为-4 (-1) 2 -3和太小说明要变大只能让left后续无论怎么夹逼这轮都找不到答案第二轮固定i 1也就是nums[i] -1left 2right 5和为-1 (-1) 2 0命中一个答案[-1, -1, 2]然后left、right--继续找这一轮里别的答案接着left 3right 4和为-1 0 1 0再命中一个答案[-1, 0, 1]再往后left和right相遇结束这一轮。第三轮如果i 2此时nums[2]还是-1和前一个固定值重复所以必须直接跳过否则会重复得到同样答案。这段手推最值得你记住的是 3 个动作排序后再做夹逼。和太小就移动左边和太大就移动右边。命中答案后不只要收缩两边还要跳过重复值。复杂度时间复杂度O(n^2)空间复杂度O(1)不计答案空间如果这是面试现场你可以这样说这题我会先排序然后把它转成“固定一个数在剩余区间里做两数之和”的问题。排序后可以用左右指针根据和的大小移动并且方便去重。整体复杂度从暴力的O(n^3)降到O(n^2)。这题最关键的不是双指针本身而是排序后的去重逻辑要完整。7. 其余题模板与关键片段26. 删除有序数组中的重复项快慢指针维护“已去重区间”的结尾intslow1;for(intfast1;fastnums.length;fast){if(nums[fast]!nums[fast-1]){nums[slow]nums[fast];slow;}}returnslow;11. 盛最多水的容器核心不是枚举每一对而是用左右指针逼近intleft0;intrightheight.length-1;intbest0;while(leftright){intarea(right-left)*Math.min(height[left],height[right]);bestMath.max(best,area);if(height[left]height[right]){left;}else{right--;}}为什么移动较短板是这题必须会讲的点。因为当前面积由短板决定如果你保留较短板不动只缩小宽度那么面积上界只会更差只有尝试换掉短板才有机会让最小高度变高。8. 边界、易混点与替代方案快慢指针最容易错在哪slow表示“结果区间的下一个位置”不是“当前扫描位置”。原地覆盖后别忘了处理尾部残留数据比如移动零的补零。题目如果要求保持相对顺序就不能随意交换元素。左右指针最容易错在哪不是看心情移动某一边而是看“移动后有没有机会更接近目标”。前提通常是有序或者经过排序后具备单调性。left right和left right不能乱换多数配对题用前者。三数之和为什么总有人写错漏掉i的去重。命中答案后只移动一边没有两边一起收缩。命中答案后没有继续跳过重复值导致重复答案。这类题怎么判断值不值得用双指针原地删除、移动、压缩优先想快慢指针。有序数组、配对、最优面积、和问题优先想左右指针。如果没有单调性、也不需要原地维护区间双指针未必是最优路线。9. 你学完后怎么验证自己真的会了不要只看懂代码。更有效的做法是用下面 3 组自测判断你是否真的掌握10 分钟内独立写出移动零并能解释slow为什么始终指向“下一个非零该放的位置”。看到三数之和时能主动说出“先排序、固定一个数、左右夹逼、三层去重”。看到盛最多水的容器时能解释为什么移动短板不会漏掉最优解。如果你在自测时出现下面任意一种情况就说明还没有真正掌握能看懂代码但自己从空白开始写不出来。知道是双指针但不知道该用快慢还是左右。代码大体能跑但一到边界、去重、循环条件就不稳。比较稳妥的过关标准是你能在 15 分钟内做出一道基础双指针题并且在 1 分钟内口述题型判断、指针含义、移动逻辑和复杂度。10. 错题本记录方式双指针题错题本重点记录我是快慢指针没想出来还是左右指针不会证明。这题的双指针不变量是什么。我到底漏了哪个去重条件。哪一步边界最容易写反比如left right还是left right。11. 适用范围与边界本文默认你已经会 Java 数组遍历、循环和基础排序。本文重点服务于 Java 实习面试、LeetCode 高频 Easy / Medium 和常见笔试基础题。本文不展开证明型题解写法、竞赛技巧或更复杂的多指针变体。如果你当前连“排序后为什么能用左右夹逼”都说不清建议先拿两数之和 II、移动零、盛最多水的容器这几题把基本模型练顺再去刷更复杂的变形题。12. 面试前 3 分钟速记原地覆盖、删除元素、移动元素优先想快慢指针。有序数组、配对、最优面积、和问题优先想左右指针。快慢指针先定义slow表示什么。左右指针先说明为什么移动某一边不会漏解。三数之和记住排序、固定一个数、左右夹逼、三层去重。13. 结尾把“会套模板”变成“会判断、会证明、会自测”双指针之所以适合作为第二类高频题型不是因为它代码短而是因为它特别能训练你的题型判断力。你真正要练成的不是背下两个模板而是形成几个稳定动作先判断这题到底是快慢指针还是左右指针。写代码前先定义每个指针分别表示什么。移动指针时能解释为什么不会漏答案。做完题后能反过来检查边界、去重和循环条件。如果这几个动作稳定下来后面的滑动窗口、链表、二分和回溯你都会更容易进入状态。感谢阅读记得点赞、关注、收藏欢迎各位评论区交流下一期疯狂码字ing……