N数之和问题的解决

N数之和问题的解决

最近在刷leetcode时重刷了一遍两数之和、三数之和、四数之和以及最接近的三数之和4道题目,在思路明确之后这种题都不再是问题啦!

  1. 题目一:二数之和(#1)
    题目要求就不说了,这是题目链接:二数之和
    解释: 这道题要求返回数组中和为target的元素的位置,这里主要想解释双指针的思想,所以hash表的方法就不介绍了,题目中本身确定数组中不存在重复元素,这其实大大减小了难度,这里我们假设给定数组的元素是可以重复的同时要求返回所有不重复的满足要求的答案。
    思路: 首先这种题目我们先对数组进行排序,时间复杂度O(nlgn),从双指针(lo,hi)的角度看,我们可以分别在数组开始与结束设置指针(这里的指针不同于C++/C里面的指针只表示位置的概念),只要左指针小于右指针,说明可能存在为发现的解,在每一次循环中,总共只有三种情况:如果两个指针对应值求和等于target,将此时的指针放入结果数组中;如果指针对应值求和小于target,这时因为我们对数组进行排序过,所以令lo指针右移;同理,求和大于target,则令hi左移动。
    此时存在一个亟待解决的问题,由于数组可能存在重复元素而输出结果要求不重复,所以我们通过某些方式保证每一个结果都是不重复的,how to do that?我们可以发现这种问题只会出现了有两个或以上连续重复的操作重复的元素,所以去重必不可少。
// 两数之和双指针解法
vector<vector<int>> twoNum(vector<int> nums, int target){
	vector<vector<int>> res; //保存结果
	sort(nums.begin(), nums.end()); //对数组进行排序
	int lo=0, hi=nums.size()-1; //定义指针
	while(lo<hi){
		int left = nums[lo]; int right = nums[hi];
		if(left + right == target){
			res.push_back({lo, hi}); //添加结果
			lo++; hi--; //更新指针
		}
		else if(left + right < target){
			while(lo<hi && nums[lo]==left) lo++; //去重并更新指针
		}
		else{
			while(lo<hi && nums[hi]==right) hi--; //去重并更新指针
		}
	}
	return res;	
}
  1. 题目二:三数之和(#15)
    同理,不说题目了,题目链接:三数之和
    解释: 这道题目难度同样有所降低,题目要求中三数之和固定为零,所以在对数组排序后,如果第一个元素大于0就不需要在进行后面的判断了,直接return结果就可。
    思路: 如果理解两数之和的双指针思路,那么三数之和很好理解,把三数之和理解成1+2数之和,什么意思呢,就是说本质还是二数之和,只不过target不再是固定不变的了,而是根据第一个数不断变化的,这么一想是不是觉得三数之和很容易呢,但是去重的方式相对于上述二数之和额外需要考虑第三个数的影响,换句话说就是要保证每一次进行那二数之和的target不能一样,ok下面代码环节。
vector<vector<int>> threeSum(vector<int>& nums, int target) {
        vector<vector<int>> res; //保存三数之和的结果
        vector<vector<int>> temp; //每一次进行二数之和的结果
        sort(nums.begin(), nums.end());
        for(int i=0; i<nums.size()-3+1; i++){
            temp = {};
            //额外判断需要去重的方式,
            //思想:保证每一次二数之和的target都不同
            if(i>0 && nums[i] == nums[i-1]) continue;
            //二数之和的判断
            twoSum(temp, nums, i+1, target-nums[i]);
            for(auto t:temp){
                t.push_back(nums[i]);
                res.push_back(t);//添上最开始那个数,得到最终结果
            }
        }
        return res;
     }
	//下面为二数之和的算法,上面已经解释过了
     void twoSum(vector<vector<int>> &temp,vector<int>& nums, int start, int target){
         int lo = start; int hi = nums.size()-1;
         while(lo<hi){
            int left = nums[lo]; int right = nums[hi];
            if(right+left == target){
                temp.push_back({left, right});
                lo++;hi--;
            }
            else if(right+left < target){
                while(lo<hi && nums[lo]==left) lo++;
            }
            else{
                while(lo<hi && nums[hi]==right) hi--;
            }
         }
     }
  1. 题目三:四数之和(#18)
    题目链接:四数之和
    思路: 。。。大家应该注意到了,2-N数之和不就是一个递归么?只要我会算二数之和,我就会三数之和,然后。。。我就会N数之和,没啥问题,直接冲代码!
//四数之和求解,不加注释了,如果看不懂建议从头开始!!!
vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        if(nums.size()<4) return res;
        sort(nums.begin(), nums.end());
        for(int i=0; i<nums.size()-3; i++){
            if(i > 0 && nums[i] == nums[i-1]) continue;
            for(int b=i+1; b<nums.size()-2; b++){
                if(b>i+1 && nums[b]==nums[b-1])continue;
                int lo = b+1; int hi = nums.size()-1;
                while(lo<hi){
                    int left = nums[lo]; int right = nums[hi];
                    if(nums[lo]+nums[hi]<target-nums[i]-nums[b]){
                        while(lo<hi && nums[lo]==left) lo++;
                    }
                    else if(nums[lo]+nums[hi]>target-nums[i]-nums[b]){
                        while(lo<hi && nums[hi]==right) hi--;
                    }
                    else{
                        res.push_back({nums[i],nums[b],left,right});
                        while(lo<hi && nums[hi]==right) hi--;
                        while(lo<hi && nums[lo]==left) lo++;
                    }
                }
            }
            
        }
        return res;
  1. 题目四:最接近的三数之和(#16)
    题目链接:最接近的三数之和
    思路: 这道题和三数之和很像,核心思想还是双指针,唯一的区别在于需要在每一次的双指针的循环中都需要额外进行一步判断,判断当前和target的差距是不是最小的,并相应进行更新!
nt threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int min;
        int res;
        for(int i=0; i<nums.size()-2; i++){
            int lo = i+1; int hi = nums.size()-1;
            while(lo<hi){
                int addres = nums[lo]+nums[hi]+nums[i];
                //判断是不是最近的结果
                if(abs(addres-target)<min) {min=abs(addres-target);res=addres;}
                if(addres<target) lo++;
                else if(addres>target) hi--;
                else return target;
            }
        }
        return res;
    }

如果你看到了这里,并且理解了N数之和求解方法的话,就给我点个赞把!
不说了继续刷leetcode!!

PS: 上述代码我是修改过的,主要还是为了理解算法,所以想要在leetcode上AC的话还是要额外修改的,比如判断特殊情况balabala···

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BerUler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值