力扣:三数之和(左右双指针思路+动画演示+代码实现)

题目

在这里插入图片描述

①双指针思路(双指针匹配方式,还不涉及去重 )

1.需要的变量个数(三个变量,双指针作为其中两个)

left、right已经两个变量,表示两个数。
题目求三数之和,只需要另外一个变量 i 即可!

所以一共是 nums[i]、nums[left]、nums[right]。存储满足条件的这三个值

2.双指针工作原理

三数之和:(三个变量之和)

①和等于0,加入结果集,并且slow也要右移
②和小于0,slow右移(无法移动就i右移)
③和大于0,fast左移

3.必须对原数组进行排序

双指针的工作原理,特别像二分查找。都是依据原数组有序性的!
所以原数组必须先进行排序才能使用双指针法

4.动画理解双指针工作方式(含去重,图里的其实是set容器去重方式)

三数之和(双指针法思路)

②代码实现建议(含去重思路两种,主要介绍双指针去重)

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;
    }
};
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值