15. 三数之和
两数之和可以用巧思也可以用map
三数之和会更加复杂一点,且这道题还需要考虑避免重复答案!
思路:
- 特判:检如果
nums
为null
或长度小于 3直接返回空数组。 - 排序:使用
sort
对数组进行升序排序。就变成了(-x,0,x)这样子的顺序。 - 遍历数组:遍历数组确定第一个数
cur=num【i】
。如果当前数和前一个数相同跳过(去重);如果cur
大于 0,直接break
,因为后面加上任何更大的数和都不会是 0了。 - 双指针:初始化两个指针
l
和r
(分别指向cur后续数的最左和最右数)。进入while
循环(条件是l<r),计算三数之和sum。
- 如果
sum === 0
:找到一个三元组,添加到结果res
中。然后进行去重,移动左指针l
和右指针r
,直到遇到不同的值。(这个很重要!!) - 如果
sum < 0
:左指针l++
,增加总和。 - 如果
sum > 0
:右指针r--
,减少总和。
代码:
//排序+双指针
var threeSum = function (nums) {
let res = [];
let len = nums.length;
if (nums == null || len < 3) return res;
nums.sort((a, b) => a - b);//升序排序(-x,0,x从小到大)
for (let i = 0; i < len; i++) {
let cur = nums[i];//第一个数(当前数)
if (nums[i] > 0) break;//和后面的数相加也不会会是0
if (i > 0 && cur == nums[i - 1]) continue;//去重(注意要判断i>0,因为第一个数没有前一个数)
let l = i + 1, r = len - 1;//双指针,当前数之后数的前后结点
while (l < r) {
const sum = nums[l] + nums[r] + cur;
if (sum === 0) {
res.push([cur, nums[l], nums[r]])
while (l < r && nums[l] == nums[l + 1]) l++;//去重
while (l < r && nums[r] == nums[r - 1]) r--;//去重
l++;
r--;
} else if (sum < 0) {
l++;
} else {
r--;
}
}
}
return res;
};
注意,我这里一开始有想过,直接在找三元组的时候,对num用set去重,这样子是不行的,因为题目所说的:
指的是同一个位置的数不能重复出现在三元组中,只是下标不能相同,但不是值不能相同。
所以需要去重的是最终的组合,而不是值。