“面试官问三数之和,我只用了 10 行核心逻辑。”
在 LeetCode 的浩瀚题海中,有两道题堪称双指针算法的启蒙导师——
👉 167. 两数之和 II - 输入有序数组
👉 15. 三数之和
它们看似简单,却藏着去重、剪枝、边界控制三大高频考点。今天,我们就用前端工程师的视角,拆解这两道题的精华逻辑,并提炼出一套可复用的双指针模板!
🔍 一、两数之和 II:有序数组的最优解
题目要求:在一个升序排列的整数数组中,找到两个数,使其和等于 target,返回它们的 1-based 索引。
✅ 核心思想:左右夹逼
因为数组已排序,我们可以:
- 左指针
left从开头出发(最小值) - 右指针
right从末尾出发(最大值) - 若和太大 →
right--;若和太小 →left++
/**
* @param {number[]} numbers
* @param {number} target
* @return {number[]}
*/
var twoSum = function(numbers, target) {
let left = 0;
let right = numbers.length - 1;
while(left < right){
const sum = numbers[left] + numbers[right];
if(sum == target){
break;
}
if(sum > target){
right--;
}
else{
left++
}
}
return [left+1,right+1];
};
💡 为什么这题能用双指针?
因为单调性!数组有序 ⇒ 和具有方向性 ⇒ 指针移动有明确策略。
🧩 二、三数之和:从 O(n³) 到 O(n²) 的飞跃
题目升级:在无序数组中找出所有不重复的三元组,使得 a + b + c = 0。
暴力解法是三层循环,时间复杂度 O(n³),显然不行。
但如果我们先排序,就能把问题转化为 “固定一个数 + 两数之和” !
🎯 解题框架:
- 排序:
nums.sort((a, b) => a - b) - 外层循环:枚举第一个数
nums[i] - 内层双指针:在
[i+1, n-1]区间找两数之和为-nums[i]
✅ 完整实现(含去重 + 剪枝):
💡 为什么排序后不会漏解?
因为我们枚举的是值而非原始索引,只要三元组的值存在,排序后一定能找到。
🧠 三、升华:双指针的通用模板
通过这两题,我们可以抽象出一个双指针处理“和类问题”的通用模式:
/**
* @param {number[]} nums
* @return {number[][]}
*/
/**
* 找出数组中所有不重复的三元组,使得三数之和为 0。
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
nums.sort((a, b) => a - b); // 升序排序
const n = nums.length;
const ans = [];
for (let i = 0; i < n - 2; i++) {
const x = nums[i];
// 跳过重复的第一个数
if (i > 0 && x === nums[i - 1]) continue;
// 最小可能和 > 0,后续不可能有解
if (x + nums[i + 1] + nums[i + 2] > 0) break;
// 最大可能和 < 0,当前 x 太小,跳过
if (x + nums[n - 2] + nums[n - 1] < 0) continue;
let j = i + 1;
let k = n - 1;
while (j < k) {
const s = x + nums[j] + nums[k];
if (s > 0) {
k--;
} else if (s < 0) {
j++;
} else {
// 找到一个解
ans.push([x, nums[j], nums[k]]);
// 跳过重复的 nums[j]
while (j < k && nums[j] === nums[j + 1]) j++;
// 跳过重复的 nums[k]
while (j < k && nums[k] === nums[k - 1]) k--;
// 移动双指针,寻找下一组可能解
j++;
k--;
}
}
}
return ans;
};
这套逻辑还能扩展到:
- 四数之和(LeetCode 18)
- 最接近的三数之和(LeetCode 16)
- 三角形计数(LeetCode 611)
🏁 结语:算法不是背题,而是建模
很多前端同学觉得算法“离业务远”,但其实:
- 双指针 ≈ 滑动窗口 ≈ 快慢指针,都是“状态压缩”的思想;
- 去重与剪枝 ≈ 性能优化,是你写高效 React/Vue 应用的底层能力;
- 边界控制 ≈ 健壮性,正是优秀工程师的标志。
下次面试官再问“三数之和”,你不仅能写出代码,还能说出:“我用了排序 + 双指针 + 两级去重 + 双剪枝,时间复杂度 O(n²),空间 O(1)。”
这,就是技术深度。
✅ 互动时间:你在刷 LeetCode 时,还遇到过哪些“看似简单却暗藏玄机”的题目?欢迎评论区分享!