很好的一道题,难点有2,控制时间复杂度和去重操作。
去重操作可以通过先将数组排序解决,如1,0,-1,1,那么1,0,-1和0,-1,1就是重复了,这个用set去重也不简单,因为这两个数组虽然数字一样但是完全不同。所以我们将其排序为-1,0,1,1,这样1只能在第三个数字取到了,我们只要保证第三个数字不要取重复的就行了,也就是和前一次取到的第三个数字比较,如果相等直接continue。
控制时复可不简单,用双指针的操作,这里看似两个循环,但是最多也就循环n次,因为如果指针3和指针2相等了就不用再走了,因为指针2再往后走也不可能出现正确答案了。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//去重通过sort,这样子就可以通过下一个和前一个是否相等来判断是否要跳过这个
//比如-1,1,1,0
//取第二个数字的时候可以取第一个1和第二个1,所以在取第二个数字的时候要判断和前一次取的第二个数字是否相等
//用first和second,third来存储下标,这样可以和前一次取的是否一样
//准确的说就是双指针,确定1后,2和3就是两个指针,缩小区间的同时要记得去重
vector<vector<int>> res;
if(nums.size() < 3) return res;
sort(nums.begin(),nums.end());
for(int first = 0; first < nums.size(); ++first){
if(first>0 && nums[first] == nums[first-1]){
continue;
}
int third = nums.size()-1;
int target = -nums[first];
for(int second = first+1; second < nums.size(); ++second){
if(second > first+1 && nums[second] == nums[second-1]){
continue;
}
//2已经去重
while(second < third && nums[second] + nums[third] > target){
third--;
}
//如果指针相等,随着b还要往后移,就不可能找的到别的解了,所以直接break
if(second == third) break;
if(nums[second] + nums[third] == target){
res.push_back(vector<int>{nums[first],nums[second],nums[third]});
}
}
}
return res;
}
};
二刷,dfs超时了。
三指针,三指针,三指针。重要的三指针说三遍。first和second记得去重!!如果取的不是第一位,就判断是否和前一位相等,相等就直接continue。
class Solution {
public:
//n是现在组合中有几个元素
// void dfs(vector<int>& nums, int index, int sum){
// if(tmp.size() == 3){
// //这样去重肯定超时
// if(sum == 0 && find(res.begin(),res.end(),tmp) == res.end()){
// res.push_back(tmp);
// }
// tmp.pop_back();
// return;
// }
// for(int i = index; i < nums.size(); ++i){
// tmp.push_back(nums[i]);
// dfs(nums,i+1,sum+nums[i]);
// }
// //将这一层的子结点循环完,再将这一层的结点弹出去
// tmp.pop_back();
// }
vector<vector<int>> threeSum(vector<int>& nums) {
//不重复很重要!!!是不重复组合,而不是不重复元素,比如(-1,-1,-1,2),那么-1,-1,2就这么一种情况,-1,2,-1就是相同的情况,在递归的同一层不能选相同的元素
//不能这么简单,-1,-1,0,1就会有相同情况产生,在第一层选两个-1
//这种就是妥妥的dfs
vector<vector<int>> res;
if(nums.size() < 3) return res;
sort(nums.begin(),nums.end());
// dfs(nums,0,0);
// return res;
//三个指针,确定第一个指针后,将target改为-nums[first],后面两个加起来等于这个就行了
for(int first = 0; first < nums.size(); ++first){
//第一层就不能选重复的
if(first > 0 && nums[first] == nums[first-1]) continue;
int target = 0-nums[first];
//要在外层就确定3,不能每次移动2就重新来一个3,3和2是并列关系
int third = nums.size()-1;
//然后确定指针二和指针三,指针二从first后一个开始,指针三从最后开始,当指针二和三相等时就退出,为什么?
//当指针二往后移动时,二和三的和只会越来越大,如果二三了二还要往后移动也不可能找的到
//如果二和三的和大于target,就将三缩小,反之不管,因为二会随着指针后移
for(int second = first+1; second < nums.size() ;++second){
if(second > first+1 && nums[second] == nums[second-1]) continue;
while(second < third && nums[second] + nums[third] > target){
third--;
}
if(second == third) break;
else if(nums[second]+nums[third] == target){
res.push_back(vector<int>{nums[first],nums[second],nums[third]});
}
}
}
return res;
}
};
简单好吧,一眼就看出内部用双指针夹逼就可以减少时复,但是去重操作必须要注意!!还有排序之后才可能用上面的方法
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//暴力就是O(n^3),但是可以通过排序来减少一点时间复杂度
//先排序,然后压缩到O(n^2),先确定第一个数的情况下,内层用两边夹的手法确定另外两个数
//当和太大就让第三个数左移,如果太小就让第二个数右移,如果刚好就加入res,当两个移动到相等就continue
//非常要注意的是不能包含重复的三元组,比如排序后-1,-1,0,1,这样就会有两个-1,1,0
//所以当first和second和前一个数字相等的时候直接跳过
vector<vector<int>> res;
if(nums.size() <= 2) return res;
sort(nums.begin(),nums.end());
for(int i = 0; i < nums.size()-2; ++i){
if(i > 0 && nums[i] == nums[i-1]) continue;
int first = nums[i];
int j = i+1;
int k = nums.size()-1;
while(j < k){
//在每一层不能选和自己相同的数
if(j != i+1 && nums[j] == nums[j-1]){
j++;
continue;
}
if(nums[j] + nums[k] == -first){
res.push_back(vector<int>{first,nums[j],nums[k]});
j++;
}
else if(nums[j] + nums[k] < -first){
j++;
}
else{
k--;
}
}
}
return res;
}
};