中等LeetCode 15动画补充中引导学习
三数之和
排序 + 对撞双指针 + 去重
先排序,再枚举第一个数 a,剩下的两个数在 a 的右侧用对撞双指针找 -a;难点不在找,而在三处去重。
时间复杂度 O(n²)空间复杂度 O(1)(不含排序所需空间)
题单:面试热题
题目描述
给定一个整数数组,找出所有和为 0 且互不重复的三元组(三个数 a、b、c 满足 a + b + c = 0),返回所有这样的三元组。
学习目标
能把三数之和拆成"固定 a + 两数之和",并说清三处去重分别在防什么。
已完成 0 / 6 阶段
诊断
先问:为什么必须先排序
不排序,能不能用左右双指针找 b + c = -a?排序到底给了双指针什么能力?
建议先完成当前阶段(提示 / 检查 / 自评),再进入下一步;也可以直接跳过。
解题思路
核心思想
先排序,再枚举第一个数 a,剩下的两个数在 a 的右侧用对撞双指针找 -a;难点不在找,而在三处去重。
为什么需要它
三重循环是 O(n³)。排序之后"和偏大就往回收、偏小就往前进"有了方向感,第二、三个数可以用双指针从两端夹逼,降到 O(n²)。
第一反应:直觉方案
三层循环枚举所有组合,把和为 0 的塞进结果集合里——慢,而且去重逻辑很快就乱了。
关键洞察
关键洞察是把"三数之和"拆成"固定一个数 + 两数之和":外层枚举 a(有序后还能剪枝——a 一旦大于 0 就不可能再有解,直接停),内层套用经典的对撞双指针。去重要做在"发现解之后":跳过与刚用过的值相同的指针,而不是事后拿集合清理。
时间复杂度 O(n²)空间复杂度 O(1)(不含排序所需空间)
易错点
- 数组必须先排序,双指针的方向判断才成立
- 三处去重:外层 a 与上一个相同时跳过;找到解后左右指针都要跳过相同值
- 剪枝:排序后若当前 a > 0,后面不可能再有解,直接 break
参考代码
参考代码
1function threeSum(nums) {
2 nums.sort((a, b) => a - b);
3 const res = [];
4 for (let i = 0; i < nums.length - 2; i++) {
5 if (nums[i] > 0) break; // 剪枝
6 if (i > 0 && nums[i] === nums[i - 1]) continue; // a 去重
7 let left = i + 1, right = nums.length - 1;
8 while (left < right) {
9 const total = nums[i] + nums[left] + nums[right];
10 if (total === 0) {
11 res.push([nums[i], nums[left], nums[right]]);
12 while (left < right && nums[left] === nums[left + 1]) left++;
13 while (left < right && nums[right] === nums[right - 1]) right--;
14 left++;
15 right--;
16 } else if (total < 0) {
17 left++;
18 } else {
19 right--;
20 }
21 }
22 }
23 return res;
24}
逐步演示
这道题目前是索引版:题面、解题思路与双语言代码已是完整内容; 逐步动画会按计划补充。