给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1] 输出:[] 解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0] 输出:[[0,0,0]] 解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
题目的意思即取元素a、b、c使得a+b+c=0,将符合的所有三元组{a,b,c},并且三元组之间要去重。由于结果中只需要返回数组的元素值而不需要返回该元素值对应的下标,因此首先可以对数组元素排序,这样可以方便我们后面取数时a<=b<=c,即按升序依次取a、b、c,思路更清晰。
解法如下:
法一(暴力解):最直接的思路肯定是三重for循环暴力枚举,时间复杂度为O(n^3),但是会超时过不了。
代码如下:
vector<vector<int>> threeSum(vector<int>& nums){//暴力解O(n^3)
vector<vector<int>> result;//存储结果三元组
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++){
if(nums[i]>0)continue;//最小的a大于0时,三数之和必定大于0,直接跳过
if(i>0&&nums[i]==nums[i-1])continue;//去重a
for(int j=i+1;j<nums.size();j++){
if(j>i+1&&nums[j]==nums[j-1])continue;//去重b
for(int k=j+1;k<nums.size();k++){
int sum=nums[i]+nums[j]+nums[k];
if(sum==0){
result.push_back({nums[i],nums[j],nums[k]});
break;//a,b确定时,只有一种c,直接跳出第三重循环,即去重c
}
}
}
}
return result;
}
法二(双指针法):不同于暴力解的三重循环依次取a,b,c,双指针法是取定a后,同时取b和c,因此只需要两重循环,时间复杂度O(n^2)。基本思路是数组排完序后,先取定a后,定义left指针指向a元素的下一个位置元素(即数组中>=a的所有数中最小的那个元素)并记为b,right指向数组最后一个元素(即>=a的所有数中最大的那个元素)并将其记为c。此时若a+b+c>0说明结果大了,此时只能让right右移才可能使得结果减小;若此时a+b+c<0,此时让left右移使得结果增大;若a+b+c=0,此时{a,b,c}即为所求三元组,存下来。left和right其实可以抽象成一个窗口的前后边沿,此时找到符合的三元组后要把窗口缩小继续探查是否在a固定时,还有新的b,c组合。
其实这道题找符合条件的三元组容易,难点是怎么去重。
对a的去重思路:取a的时候,如果上一轮取的a和这一轮取的a一样,直接跳过,因为这一轮取定a后,找到的符合条件的三元组一定是和前一轮找到的相重复。
对b、c的去重思路:找到符合条件的三元组并存储后,要相应内缩窗口,此时只需要把left和right移动到全新的b、c处,即内缩窗口时要跳过所有本轮已经存储过的b,c。
代码如下:
vector<vector<int>> threeSum(vector<int>& nums){
vector<vector<int>> result;//存储结果三元组
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++){
if(nums[i]>0)continue;
if(i>0&&nums[i]==nums[i-1])continue;//对a去重
int left=i+1;
int right=nums.size()-1;
while(left<right){
int sum=nums[i]+nums[left]+nums[right];
if(sum>0)right--;
else if(sum<0)left++;
else{
result.push_back({nums[i],nums[left],nums[right]});//将a,b,c存入三元组
while(left<right&&nums[left]==nums[left+1])left++;//对b去重
while(left<right&&nums[right]==nums[right-1])right--;//对c去重
right--;
left++;//内缩窗口
}
}
}
return result;
}