①双指针思路(双指针匹配方式,还不涉及去重 )
1.需要的变量个数(三个变量,双指针作为其中两个)
left、right已经两个变量,表示两个数。
题目求三数之和,只需要另外一个变量 i 即可!
所以一共是 nums[i]、nums[left]、nums[right]。存储满足条件的这三个值
2.双指针工作原理
三数之和:(三个变量之和)
①和等于0,加入结果集,并且slow也要右移
②和小于0,slow右移(无法移动就i右移)
③和大于0,fast左移
3.必须对原数组进行排序
双指针的工作原理,特别像二分查找。都是依据原数组有序性的!
所以原数组必须先进行排序才能使用双指针法!
4.动画理解双指针工作方式(含去重,图里的其实是set容器去重方式)
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="ad1JTctn-1721391579061" src="https://live.csdn.net/v/embed/410921"></iframe>三数之和(双指针法思路)
②代码实现建议(含去重思路两种,主要介绍双指针去重)
1.不要一开始就同时考虑匹配三数之和、去重。这两种功能
同时考虑这两种操作会导致:
逻辑复杂,难以实现,逻辑出错率极高(本人亲身体验)
2.先实现"三数之和结果集的存储"功能
class Solution {
public:
vector<vector<int>>result;
//三数之和
//第一点
//单次匹配思路:找满足条件的 nums[i]、nums[left]、 nums[right]。//i<left<right
//第二点
//必须先查找完成之后,在考虑去重操作,不要两个操作一起考虑,很容易逻辑混乱,导致做不出!
vector<vector<int>> threeSum(vector<int>& nums)
{
//双指针解决本问题需要排序
//为什么要排序:双指针工作原理(类似二分查找,需要有序性)
sort(nums.begin(),nums.end());
result.clear();
for(int i = 0;i<nums.size()-2;i++){
if(nums[i]>0)return result;//剪枝操作(第一个元素最小,如果都是正数,正数相加只会更大)
int slow = i+1;
int fast = nums.size()-1;
while(slow<fast){//这里体现左右双指针,所以不会是i在left,right区间内
if(nums[i]+nums[slow]+nums[fast]==0){//满足三数之和的情况
result.push_back({nums[i],nums[slow],nums[fast]});
slow++;
}
else if(nums[i]+nums[slow]+nums[fast]>0){ fast--;} //和太大的情况}
else{ slow++; }//和太小的情况
}
}
return result;
}
};
3.再实现"结果集去重"功能
Ⅰ:set容器去重(效率会比较低)
比较方便实现,代码几没有改动
本来是使用二维数组存储,改成set<vector<int>>存储即可!最后返回值再改成二维数组
Ⅱ:利用双指针去重(效率比较高)
1.需要积累经验,第一次写不出很正常。
所有变量都要去重(本题三数之和,三个变量都要去重)
2.收集结果集之后再去重
反证法如下:
进行 nums[i]的去重。必须是收集完结果集之后再去重。
举例:-2,-2,-2,0,2,2,4
结果集含 {-2,-2,4}。如果先去重,一定就排除了nums[slow]==nums[i]的情况(结果集又包含这种情况)
3.补充:(如果说法有误,虚心更正哈)
了解即可,代码随想录是先对 i 进行去重,但左右指针去重是收录结果后!
(而我把三个变量都统一成收录结果后去重了)
其实如果去重的方式不影响后续的值(代码随想录写法:i去重不影响slow)那么就可以先去重
③具体代码实现(两种版本,注释也很详细)
1.第一种版本(set容器,效率比较低,但是很好实现,代码几乎没有变动)
修改地方只有四处。
class Solution {
public:
set<vector<int>>result;//第一处修改
//三数之和
//第一点
//单次匹配思路:找满足条件的 nums[i]、nums[left]、 nums[right]。//i<left<right
//第二点
//必须先查找完成之后,在考虑去重操作,不要两个操作一起考虑,很容易逻辑混乱,导致做不出!
vector<vector<int>> threeSum(vector<int>& nums)
{
//双指针解决本问题需要排序
//为什么要排序:双指针工作原理(类似二分查找,需要有序性)
sort(nums.begin(),nums.end());
result.clear();
for(int i = 0;i<nums.size()-2;i++){
//第二处修改
if(nums[i]>0)return {result.begin(),result.end()};//剪枝操作(第一个元素最小,如果都是正数,正数相加只会更大)
int slow = i+1;
int fast = nums.size()-1;
while(slow<fast){//这里体现左右双指针,所以不会是i在left,right区间内
if(nums[i]+nums[slow]+nums[fast]==0){//满足三数之和的情况
result.insert({nums[i],nums[slow],nums[fast]}); //第三处修改,set容器使用insert
slow++;
}
else if(nums[i]+nums[slow]+nums[fast]>0){ fast--; }//和太大的情况}
else{ slow++; }//和太小的情况
}
}
return {result.begin(),result.end()};//第四处修改
}
};
2.第二种版本(双指针去重,效率高,满满的经验!)
class Solution {
public:
//三数之和
//第一点
//单次匹配思路:找满足条件的 nums[i]、nums[left]、 nums[right]。//i<left<right
//第二点
//必须先查找完成之后,在考虑去重操作,不要两个操作一起考虑,很容易逻辑混乱,导致做不出!
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>>result;
//双指针解决本问题需要排序
//为什么要排序:双指针工作原理(类似二分查找,需要有序性)
sort(nums.begin(),nums.end());
for(int i = 0;i<nums.size()-2;i++){
if(nums[i]>0)return result;//剪枝操作(第一个元素最小,如果都是正数,正数相加只会更大)
int slow = i+1;
int fast = nums.size()-1;
while(slow<fast){//这里体现左右双指针,所以不会是i在left,right区间内
if(nums[i]+nums[slow]+nums[fast]==0){//满足三数之和的情况
result.push_back({nums[i],nums[slow],nums[fast]});
while(slow<fast&&nums[slow]==nums[slow+1]){//slow去重
slow++;
}
while(slow<fast&&nums[fast]==nums[fast-1]){//fast去重
fast--;
}
slow++;
fast--;//不写fast--也行!
}
else if(nums[i]+nums[slow]+nums[fast]>0){ fast--;} //和太大的情况}
else{ slow++; }//和太小的情况
}
//进行 nums[i]的去重。必须是收集完结果集之后再去重。
//举例:-2,-2,-2,0,2,2,4
//结果集含 -2,-2,4。没办法去重后再收集结果,因为nums[slow] == nums[i]//往这个方面思考就知道了!
while(nums[i]==nums[i+1]&&i<nums.size()-2){
i++;
}//还有一种去重方式 if(nums[i]==nums[i-1])//具体看:https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html#%E6%80%9D%E8%B7%AF
}
return result;
}
};
标签:slow,nums,三数,左右双,fast,力扣,result,指针
From: https://blog.csdn.net/kchick/article/details/140558170