力扣:四数之和(双指针思路,本质是三数之和+代码实现+去重逻辑解释)

题目

在这里插入图片描述

①思路(本质)

1.本题的本质:三数之和

2.思路

三数之和的代码基础上外加一层循环,就是四数之和!

三数之和变量:i、left、right

四数之和变量:j、i、left、right
四数之和变量:j、三数之和变量(从嵌套视角看待)

②代码分步实现(不要多功能同时实现)

同样的先考虑存储结果集,再进行去重

代码跟复制粘贴差不多了!只是去重优化了一下

1.先实现"存储结果集"

本题可以做二级剪枝!

 小经验:双指针外面有多少层循环,就可以做多少级剪枝!
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
       vector<vector<int>>result;
       sort(nums.begin(),nums.end());//双指针做类似于二分查找的操作,一定要排序 

       //三数之和外嵌套一层代码
       for(int j=0;j<nums.size()-3;j++){//for(int j=0;j<nums.size()-3;j++){//这是我写的初代版本,但是需要分类讨论//所以改成了更一般化的情况
       		if(nums[j]>=0&&nums[j]>target)break;
       		
       		//下面就是三数之和的代码
       		for(int i = j+1;i<nums.size();i++){//for(int i = j+1;i<nums.size()-2;i++){//这也是我的写的初代版本
       			if (nums[j] + nums[i] > target && nums[j]>= 0) {
                    break;
                }
       			int left = i+1;
       			int right = nums.size()-1;
       			while(left<right){
       				
       				int sum = nums[j]+nums[i]+nums[left]+nums[right];
				  	if(sum==target){
				   		result.push_back({nums[j],nums[i],nums[left],nums[right]});
				
				   		left++;
				   }
				   
				   else if(sum<target){ left++; }
				   
				   else{ right--; }
				   
				}
			}
       		
	   }
	   return result;
    }
};

2.实现去重操作(两种方式:set容器、双指针去重)

具体看文章下面的代码实现,整体思路都跟三数之和一样!

③补充本题细节

在这里插入图片描述

1.本题相比三数之和修改了for循环的终止范围

本题元素的个数并不是至少4个。(可能只有一个元素,但要求四数之和!)

nums.size()-3。因为返回值是无符号类型,所以元素太少返回的值是巨大的。终止条件就不对!
改成nums.size()通用。(如果分类讨论nums.size()-3也行,如果元素小于4个就直接返回空二维数组)

在这里插入图片描述

2.本题案例累加完超过int取值范围,需要转化为long类型!

在这里插入图片描述

④具体代码实现

1.set容器去重方式实现(优点同三数之和,实现巨方便,性能比较差)

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
       set<vector<int>>result;
       sort(nums.begin(),nums.end());//双指针做类似于二分查找的操作,一定要排序 

       for(int i=0;i<nums.size();i++){
            if(nums[i]>=0&&nums[i]>target)break;
       		for(int j = i+1;j<nums.size();j++){
       			if (nums[j] + nums[i] > target && nums[j]>= 0) {
                    break;
                }
       			int left = j+1;
       			int right = nums.size()-1;
       			while(left<right){
       				
       				long sum = (long)nums[i]+nums[j]+nums[left]+nums[right];
				  	if(sum==target){
				   		result.insert({nums[i],nums[j],nums[left],nums[right]});
				   		
				   		left++;
				   }
				   
				   else if(sum<target){ left++; }
				   
				   else{ right--; }
				   	
				}
       			
			}
       		
	   }
	   return {result.begin(),result.end()};
       
    }
};

2.双指针去重(优点同三数之和,实现不方便,性能高)

需要对四个元素去重

亲手写出三数之和去重的双指针代码,四数之和也如同照葫芦画瓢!

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
       vector<vector<int>>result;
       sort(nums.begin(),nums.end());//双指针做类似于二分查找的操作,一定要排序 

       //三数之和外嵌套一层代码
       for(int j=0;j<nums.size();j++){//for(int j=0;j<nums.size()-3;j++){//这是我写的初代版本,但是需要分类讨论//所以改成了更一般化的情况
       		if(nums[j]>=0&&nums[j]>target)break;//一级剪枝
       		//这就是三数之和的代码
       		for(int i = j+1;i<nums.size();i++){//for(int i = j+1;i<nums.size()-2;i++){//这也是我的写的初代版本
       		
       			if (nums[j] + nums[i] > target && nums[j]>= 0) {//二级剪枝
                    break;
                }
       			int left = i+1;
       			int right = nums.size()-1;
       			while(left<right){
       				
       				long sum =(long) nums[j]+nums[i]+nums[left]+nums[right];//四个数案例存在过大的情况,需要转化成(long类型)
				  	if(sum==target){
				   		result.push_back({nums[j],nums[i],nums[left],nums[right]});
						
						while(left<right&&nums[left]==nums[left+1]){ left++; }//left去重
						while(left<right&&nums[right]==nums[right-1]){ right--; }//right去重
						
				   		left++;
                        right--;
				   }
				   
				   else if(sum<target){ left++; }
				   
				   else{ right--; }
				   
				}
				
                //i变量去重
				while(i<nums.size()-1&&nums[i]==nums[i+1]){ i++; }//这里不能i<nums.size()//防止数组越界
				
			}
       		
            //j变量去重(你应该体会到了,只是嵌套一层套路而已!
       		while(j<nums.size()-1&&nums[j]==nums[j+1]){ j++; }//这里不能i<nums.size()//防止数组越界
	   }
	   return result;
    }
};

⑤拓展

可以拓展到N数之和,到时候使用回溯算法为双指针嵌套循环即可!
感兴趣的朋友可以上b站了解一下回溯算法!也可以上代码随想录

⑥具体讲讲我的去重逻辑!

双指针内的去重比较容易,也跟外层去重差不多。所以我只讲外层去重

以下图去重代码为例,为变量 j 去重

在这里插入图片描述

执行完去重后(刚好完成while时),
(如果可以去重)其实 j 指向最后一个可去重的元素(没完全去重),
但是: j 此时处于循环体的尾部,不会匹配目标值,要进入下一个循环才会匹配,而进入下一个循环就真正的做到去重!,然后执行匹配是否满足四数之和代码!

举例:-4,-4,-4,-4,0,1,2,3。
当j = 0,(易得:其他三个变量恒定不变)
加入结果集之后开始执行去重,最终:
j=3,此时nums[j]=4。nums[j+1]=0。(循环体尾部,所以不会匹配target值)
进入下一次循环 j=4,nums[j]=0,然后匹配是否sum ==target值(达到去重目的)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值