“两数之和我还能暴力,三数之和?那不得 O(n³) 爆炸?”
别慌!今天带你用排序 + 双指针,把三数之和从“地狱难度”变成“轻松拿捏”。
🧠 问题描述
给定一个整数数组 nums,判断是否存在三个元素 a, b, c,使得 a + b + c = 0。
要求:找出所有不重复的三元组。
例如:
输入: nums = [-1, 0, 1, 2, -1, -4]
输出: [[-1, -1, 2], [-1, 0, 1]]
注意:结果中不能包含重复的三元组!
💥 暴力解法:O(n³) 的“青春疼痛”
最直觉的做法?三层 for 循环,枚举所有可能的三元组:
for (let i = 0; i < n; i++)
for (let j = i + 1; j < n; j++)
for (let k = j + 1; k < n; k++)
if (nums[i] + nums[j] + nums[k] === 0) ...
时间复杂度:O(n³)
空间复杂度:O(1)
但现实很骨感——LeetCode 直接给你一个超时大礼包 💣。
而且去重?想想就头大:怎么保证 [ -1, 0, 1 ] 和 [ 0, -1, 1 ] 不算两次?
所以,是时候请出我们的主角了 👉 排序 + 双指针!
🌟 核心思想:先排好队,再“夹击”
第一步:排序(O(n log n))
为什么排序?因为:
- 有序数组能让我们用双指针“聪明地”移动
- 重复元素会挨在一起,方便跳过
nums.sort((a, b) => a - b); // 升序排列
小知识:JavaScript 的
Array.sort()默认按字符串比较,所以必须传比较函数!
第二步:固定一个数,双指针找另外两个
想象你在玩“三明治”游戏:
- 最左边是固定的“底层面包”(
nums[i]) - 中间夹着“生菜”(
left)和“火腿”(right) - 目标:让三者加起来正好等于 0!
具体操作:
-
遍历
i从0到n-3(因为后面至少要留两个位置) -
跳过重复的
nums[i](避免重复三元组) -
设置
left = i + 1,right = n - 1 -
计算
sum = nums[i] + nums[left] + nums[right]- sum == 0 → 找到答案!加入结果,并同时移动左右指针,还要跳过重复值
- sum < 0 → 太小了,
left++(增大) - sum > 0 → 太大了,
right--(减小)
✨ 代码实现(带注释版)
function threeSum(nums) {
// 1. 排序!这是魔法的开始
nums.sort((a, b) => a - b);
const res = [];
// 2. 固定第一个数
for (let i = 0; i < nums.length - 2; i++) {
// 跳过重复的起点(避免重复三元组)
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1;
let right = nums.length - 1;
// 3. 双指针夹击
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum === 0) {
res.push([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--;
}
else if (sum < 0) {
left++; // 太小,往右走
}
else {
right--; // 太大,往左走
}
}
}
return res;
}
🎯 为什么这样不会漏解?
因为数组已排序,对于固定的 nums[i]:
- 如果
sum < 0,说明当前left太小,只有增大 left 才可能接近 0 - 如果
sum > 0,说明right太大,只有减小 right 才可能接近 0
这就像在一条有序的数轴上,用两个指针“逼近”目标值,每一步都朝着正确方向前进,绝不错过任何可能!
⏱️ 时间 & 空间复杂度
- 时间复杂度:O(n log n)(排序) + O(n²)(外层循环 × 双指针) → O(n²)
- 空间复杂度:O(1)(不考虑结果数组)
比暴力快了一个数量级!👏
😂 幽默小剧场
面试官:“你会做三数之和吗?”
我:“会!三层 for 循环,优雅又简洁。”
面试官:“……下一位。”
别做那个“下一位”!学会双指针,你就是面试官眼中的算法锦鲤 🐟。
🔚 总结
| 方法 | 时间复杂度 | 是否可行 | 适合场景 |
|---|---|---|---|
| 暴力三重循环 | O(n³) | ❌ 超时 | 面试装傻(不推荐) |
| 排序 + 双指针 | O(n²) | ✅ 优雅高效 | 实战、面试、刷题 |
记住口诀:
“三数之和先排序,固定一头双指找;
小了左移大右缩,重复跳过不烦恼。”
📌 最后
✨ 算法不难,难的是迈出第一步。而你,已经走在路上了。