题目:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:要找三个数字,和为0.首先想到三重循环,分别循环i,j,k。判断三者之和是否为0。可是要求三元组不能重复,也就是说,[-1,-1,2] 和 [2,-1,-1] 是一个意思,不能重复。但是所给的原数组是乱序的,肯定会出现同样的元素不同的顺序。所以我们最好先把数组排序,升序排列,这样遍历的时候,[a,b,c] 就会以从小到大的顺序出现。排完序后,数组变成:[-4,-1,-1,0,1,2]
我们让i 从0~length-2 ; j从i+1 ~ length-1 ; k从j+1 ~ length
var threeSum = function(nums) {
nums=nums.sort()
let result=[],now
for(let i=0;i<nums.length-2;i++){
if(i===0 || nums[i]!==nums[i-1]){
for(let j=i+1;j<nums.length-1;j++){
if(j===i+1 || nums[j]!==nums[j-1]){
for(let k=j+1;k<nums.length;k++){
if((k===j+1 || nums[k]!==nums[k-1]) && nums[i]+nums[j]+nums[k]===0){
now=[nums[i],nums[j],nums[k]]
result.push(now)
}
}
}
}
}
}
return result
};
还有一种情况要考虑;如果排完序是这样的:[-4,-1,-1,-1,0,1,2],i=1,j=2,k=6时满足条件;然后j+1,j=3,j处的值还是-1,如果还考虑,那就会出现相同的结果[-1,-1,2] 。所以如果遇到i/j/k和上一次的值相等了,那就跳过,不考虑,直到和上一次的值不相等。但是如果此时j=i+1/k=j+1,如果j和i / k和j处的值相等,这个是要考虑在内的。所以每次进入三个循环的时候,要先判断,如果当前的i/j/k是第一个,那就可以往下进行;或者如果当前的i/j/k和上一次的不相等,也可以往下进行。这两个条件只要有一个成立,就可以。
但是三重循环,时间复杂度还是O(n3),显示超时了。
所以考虑其他方案:
双指针
我们先花费一些时间把数组变成有序的。参考两数之和,使用双指针的思路。每次固定一个i,i的范围从0~nums.length-2。然后分别让j=i+1,k=nums.length-1,让j和k从两端往中间移动。只要当j<k,就一直循环判断三数之和是否等于0.如果等于,那就直接Push,然后j右移,k左移;如果小于0,就说明当前的组合要再大一点,所以右移j;如果大于0,说明要再小一点,所以左移k。
var threeSum = function(nums) {
nums=nums.sort((a,b)=>a-b)
let result=[]
for(let i=0;i<nums.length-2;i++){
if(nums[i]>0){break}
if(i===0 || nums[i]!==nums[i-1]){
let j=i+1,k=nums.length-1
while(j<k){
let sum=nums[i]+nums[j]+nums[k]
if(sum===0){
result.push([nums[i],nums[j],nums[k]])
j++
while(nums[j]===nums[j-1]){j++}
k--
while(nums[k]===nums[k+1]){k--}
}else if(sum<0){
j++
}else{
k--
}
}
}
}
return result
};