文章目录
Leetcode15
1.问题描述
2.解决方案
解法一:排序加双指针
(1)思路
1.思路就像下面那个图初始化i=0,left=i+1,right=len-1
2.然后对于每一个i找到符合条件的(i,left,right)的三元组即可,但是代码实现有很多细节需要考虑
(2)分析总结
a.关于可行性的检查,相对简单只需要在循环一开始判断就好
//检查可行性
if(nums[i]>0) break;
b.关于越界的解决
首先就是在left和right的移动中呢实际上,会出现越界的情况,那么while条件会帮我们排除一部分,那么如果不continue,会进行下面的 if 条件判断很有可能越界,那么养生好习惯,如果三个 if 只能走一个,每一个 if 结束都加上 continue ,然后就是 left++ 过程中判断 left+1 可能会越界所以在 if 中加入&&来判断。
//重复性检查
while((left+1)<len&&nums.at(left)==nums.at(left+1)) left++;
left++;
while ((right-1)>=0&&nums.at(right)==nums.at(right-1)) right--;
right--;
//非常关键,就是你变化为可能会有越界,你指望while帮你筛选,那你就赶紧continue防止下面的的if检查中出错
continue;
c.关于重复性的检查
首先呢,在固定的 i 需要有重复检查,这个比较简单如果有重复直接continue
//检查重复性
if(i!=0&&nums.at(i)==nums.at(i-1)) continue;
其次在 left right 移动中也需要重复检查,这是一开始的版本非常的傻,会重复就+2 这种简单思维杜绝,以后一说起重复就至少想成三个重复而不是两个!
//重复性检查
if(nums.at(left)==nums.at(left+1)) left=left+2;
else left++;
if(nums.at(right)==nums.at(right-1)) right=right-2;
else right--;
//非常关键,就是你变化为可能会有越界,你指望while帮你筛选,那你就赶紧continue防止下面的的if检查中出错
continue;
后面改后版本,while并且记住while结束,其实分析可知再加一次才对,其实就是以后一说起重复就至少想成三个重复而不是两个,就对了!
//重复性检查
while((left+1)<len&&nums.at(left)==nums.at(left+1)) left++;
left++;
while ((right-1)>=0&&nums.at(right)==nums.at(right-1)) right--;
right--;
(3)代码实现
//排序加双指针
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//1.必要的检查
vector<vector<int>> ans;
int len=nums.size();
if(len<3) return ans;
//2.排序
sort(nums.begin(),nums.end());
//2.
for(int i=0;i<len;i++){
//检查可行性和重复性
//if (nums[i] == nums[i + 1]) continue; //错误去重方法,将会漏掉-1,-1,2 这种情况
if(nums[i]>0) break;
if(i!=0&&nums.at(i)==nums.at(i-1)) continue;
int left=i+1;
int right=len-1;
while (left<right){
//去重复逻辑如果放在这里,(0,0,0)的情况,可能直接导致right<=left,从而漏掉了(0,0,0)这种三元组
//while (right > left && nums[right] == nums[right - 1]) right--;
//while (right > left && nums[left] == nums[left + 1]) left++;
if(nums.at(i)+nums.at(left)+nums.at(right)==0){
vector<int> v;
v.push_back(nums.at(i));
v.push_back(nums.at(left));
v.push_back(nums.at(right));
ans.push_back(v);
//重复性检查
//去重逻辑应该放在找到一个三元组之后
while((left+1)<len&&nums.at(left)==nums.at(left+1)) left++;
left++;
while ((right-1)>=0&&nums.at(right)==nums.at(right-1)) right--;
right--;
//非常关键,就是你变化为可能会有越界,你指望while帮你筛选,那你就赶紧continue防止下面的的if检查中出错
continue;
}
if(nums.at(i)+nums.at(left)+nums.at(right)>0){
while ((right-1)>=0&&nums.at(right)==nums.at(right-1)) right--;
right--;
continue;
}
if(nums.at(i)+nums.at(left)+nums.at(right)<0){
while((left+1)<len&&nums.at(left)==nums.at(left+1)) left++;
left++;
continue;
}
}
}
//3.
return ans;
}
};
解法二:排序加双指针(官方优化)
这个优化说实话,结构很乱,而且我还没看出来为啥效率提高了,在看ing
//官方题解优化
class Solution1 {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
ans.push_back({nums[first], nums[second], nums[third]});
}
}
}
return ans;
}
};
解法三:哈希
1.大致思路就是找出a+b+c=0,其中a=nums[i], b=nums[j], c=-(a+b)
2.然后set.find( c ),如果出现过证明在set种有过i,j的组合,可以形成三元组(i,j,c)
3.如果没出现过那就加入j,思路呢还可以不是很难,但是去重等等细节比较复杂,难以控制
不适合用哈希其实本题目
//哈希
class Solution2 {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//1.必要的检查
vector<vector<int>> ans;
int len=nums.size();
if(len<3) return ans;
//2.排序
sort(nums.begin(),nums.end());
//2.找出a+b+c=0,其中a=nums[i], b=nums[j], c=-(a+b)
for(int i=0;i<len;i++){
//检查可行性和a重复性
//if (nums[i] == nums[i + 1]) continue; //错误去重方法,将会漏掉-1,-1,2 这种情况
if(nums[i]>0) break;
if(i!=0&&nums.at(i)==nums.at(i-1)) continue;
unordered_set<int> set;
for(int j=i+1;j<len;j++){
//b重复性
if(j>i+2&&nums[j]==nums[j-1]&&nums[j-1]==nums[j-2]) continue;
int c=0-(nums[i]+nums[j]);
if(set.find(c)!=set.end()){
ans.push_back(vector<int>{nums[i],nums[j],c});
//c重复性
set.erase(c);
}
else set.insert(nums[j]);
}
}
//3.
return ans;
}
};