题目:给你一个整数数组nums,判断是否存在一元组 [nums [i],nums[j],nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums [i] +nums[j] + nums[k] == 0 。请你返回所有和为 且不重复的三元组
注意: 答案中不可以包含重复的三元组
解:(1)分析:
由题目可知,我们要在给的整数数组中找出和为0的三元组,要注意的是找到的三元组中不能出现重复下标。
优化1:
如果我们使用三重循环枚举出所有的三元组,然后使用哈希表去重操作,得到不包含重复三元组的最终答案,这个做法的时间复杂度和空间复杂度都很高,所以我们要将三重循环优化和修改。
为了使三数组不重复,我们保持三重循环的大框架不变的情况,将数组进行排序,保证:
- 第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;
- 第三重循环枚举到的元素不小于当前第二重循环枚举到的元素
我们枚举的三元组 (a, b, c)满足 a≤b≤c,保证了只有 (a,b,c) 这个顺序会被枚举到,而(b, a, c)、(c, b, a) 等等这些不会出现,这样就减少了重复。
优化2:
同时,对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复。举个例子,如果排完序的数组为:
[0, 1, 2, 2, 2, 2, -1]
我们枚举到的第一个三元组为(0,1,2),如果第三重循环继续枚举下一个元素,那么仍然是三元组 (0,1,2),产生了重复。因此我们需要将第三重循环「跳到」下一个不相同的元素,即数组中的最后一个元素 -1,枚举三元组 (0, 1, -1)。我们可以在循环中添加if语句判断。
优化3:
虽然已经简化一些,但我们仍旧还是三重循环的框架,为了进一步减少我们引入双指针,我们固定二重枚举的元素a和b,此时唯一的 c 满足 a+b+c = 0。如果此时的第三层循环的元素不满足,那么当新的第二重循环在b后枚举一个元素b’时,由于b’> b那么满足a+b‘+c'=0的c’一定有 c‘< c,也就是说c’一定出现在 c 的左侧。所以,我们可以认为从小到大枚举b的同时从大到小枚举 c,即第二重循环和第三重循环实际上是并列的关系.而代码的时间复杂度也从O(N3)变为O(N2)。
(2)代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n=nums.size();
vector<vector<int>>vec;
sort(nums.begin(),nums.end());
for(int first=0;first<n;first++){//枚举a
if(first>0 && nums[first]==nums[first-1]){//保证和上一次枚举的数不相同,减少重复
continue;
}
int third=n-1; //使c指针指向数组最右端
int target=-nums[first];
for(int second=first+1;second<n;second++){//枚举b
if(second>first+1&&nums[second]==nums[second-1]){//保证和上一次枚举的数不相同,减少重复
continue;
}
while(third>second&&nums[second]+nums[third]>target){//保证 b 的指针在 c 的指针的左侧
--third;
}
if(third==second){//如果指针重合,随着 b 后续的增加就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
break;
}
if(nums[second]+nums[third]==target){
vec.push_back({nums[first],nums[second],nums[third]});}
}
}
return vec;
}
};