给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
示例:给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
思路分析:
双指针法用在求和、大小比较类的数组题目里时,大前提往往是:该数组必须有序。否则双指针根本无法帮助我们缩小定位的范围,压根没有意义。因此这道题的第一步是用 sort() 将数组排序。
然后遍历数组,固定遍历到的数字,左指针指向该数字后面紧邻着的位置,右指针指向数组末尾,左右指针同时向中间移动
TL,DR
- 数组有序之后,有规律有利于做题,观察执行示例的输出数组里面就是有序的
- 有序数组使用双指针——对撞指针
- 边界条件:外层i 增加的时候,左指针右移,右指针左移,排除元素重复
在左右指针对撞之前,计算两个指针对应的数字与固定数字的和:
- 如果和等于0,得到目标组合
- 如果和小于0,说明左指针的数偏小,左指针右移
- 如果和大与0,说明右指针的数偏大,右指针左移
tips:本题目要求返回不重复的三元组,所以遍历数组的时候要加以判断
var threeSum = function(nums) {const arr = nums.sort((a, b) => a-b)const res = []const len = arr.lengthfor(let i=0;i<len-2;i++){if(i>0 && arr[i] === arr[i-1]){ // 跳过重复元素continue}let left = i+1let right = len-1while(left < right){if(arr[i]+ arr[left]+arr[right] > 0){right--// 处理右指针左移时重复的情况while(left < right && arr[right] === arr[right+1]){right--}}else if(arr[i]+ arr[left]+arr[right] < 0){left++// 处理左指针右移时重复的情况while(left < right && arr[left] === arr[left-1]){left++}}else{res.push([arr[i], arr[left], arr[right]])left++while(left < right && arr[left] === arr[left-1]){left++}right--while(left < right && arr[right] === arr[right+1]){right--}}}}return res};
在上面这道题中,左右指针一起从两边往中间位置相互迫近,这样的特殊双指针形态,被称为“对撞指针”。
当出现有序和数组——这两个关键字的时候,普通双指针走不通,立刻联想到对撞指针!对撞指针可以帮助我们缩小问题的范围,但是前提条件必须是有序的数组,所以有些时候我们发现思路走不通的时候可以先对数组进行排序,创造解题的条件。
另一种答案,自己写的,感觉边界条件更清晰
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
let res = []
const arr = nums.sort((a,b) => a-b)
const len = arr.length
for(let i=0;i< arr.length -2; i++){
if(i>0 && arr[i] === arr[i-1]){
continue
}
// 定义双指针
let left = i+1
let right = len - 1
while( left < right){
if(arr[i] + arr[left] + arr[right] === 0 ){
res.push([arr[i], arr[left], arr[right]])
left++
// 左指针右移避免重复
while(arr[left] === arr[left-1]){
left++
}
right--
}else if(arr[i] + arr[left] + arr[right] < 0){
left++
}else{
right--
}
}
}
return res
};
