四个数之和+双指针大法

目录

一、前言

二、引入(三数之和)

三、引入初解答

四、双指针的美妙之处

步骤①(sort大法好)

步骤②

步骤③(双指针的安放)

步骤④(指针的移动)

步骤⑤(去重的处理)

五、引入的完整代码

 六、四数之和

完整代码

测试用例

七、力扣的四数之和题目

①题目描述

​  ②完整代码

③AC凭证


一、前言

hello哇,鸽了好多好多天,终于蒟蒻本sheep又能够回来写博客了呢,今天给大家带来四数之和讲解!

二、引入(三数之和

假设现在有一个一维数组,里面有n个数字,可能是重复的,但是刚开始的数据是一个乱序状态,现在需要亲爱的读者,从里面找到三个不同下标的数字,使得最后三个数字加起来的和满足输入进去的一个target值,最终返回一个int数字,有多少个数满足这样的条件呢?请读者先留步思索一下应该怎么去做这样的题目呢?

三、引入初解答

其实解答引入的问题有一个很简单的想法,就是三层for循环依次控制i,j,k的位置,然后当a【i】+a【j】+a【k】=target就让我们的方案数字在每次满足条件的时候加一就好了。那么这样的方法的时间复杂度显然是o(n^{3})当题目的数据给得非常大了以后就会非常耗时间,导致很多题目过不去!!!那么在原有基础上,如果要求选出的三个数不能跟以前已经选出来的三位数重复,怎么办呢?

四、双指针的美妙之处

看到这个标题,不知道看过之前我的几期博客的同学是否对双指针已经感到非常的熟悉了,没错,这题为了减少一层循坏,用双指针就是一个妙处!怎么使用双指针呢?我们接着往下看!

步骤①(sort大法好)

在利用双指针前,我们一定要想到,数组是一个乱序的状态,如果对一个乱序的数组利用双指针,那么其实它的功效便不是那么明显了,所以我们不妨让数组先有序起来,那么就可以利用之前所介绍到的sort!

int n,target;
	cin>>n>>target;
    //数组下标从1开始的注意一下!!!!!!!!    
	for(int i=1;i<=n;i++) cin>>a[i];
	int sum=0;
    //sort排序从小到大
	sort(a+1,a+n+1);

步骤②

假设有一串已经排好序的数组,怎么利用双指针呢?在前面我们说过,双指针只能减小一层循坏的量,也就是我们猜测最终的时间复杂度变为o(n²),那么毋庸置疑的是,在你原有的基础上,最外层的for循坏是不能省略掉的(for(i)) 利用i来控制从第一个数一直到倒数第3个数,为什么是倒数第三个呢,因为双指针所指的下标还有两个数呢!

步骤③(双指针的安放)

如图,i控制最外面一层循坏,而左指针从i的下一个开始指向,右指针从最后的数开始指向,为什么可以这么做呢?左右指针不断地缩小最终相遇的过程,会把i后面的数字全部进行一遍搜索,而且,我们注意到我最终要完成的是找到所有不重复的三元组,那么左右指针在遇到重复的过程中,也可以直接跳过重复的答案,其实如果直接要用三层循坏去重,也会利用到类似指针的做法的!

int left=i+1,right=n;

步骤④(指针的移动)

因此左右指针应该怎么移动呢,其实很简单,当目前的三个数大于目标值了,右指针往左移一个单位,这样可以让整体的值往小了变。;当目前的三个数小于目标值了,让左指针往右移一个单位。如果等于的话,就让方案数加1就好了。

if(a[i]+a[left]+a[right]>target) right--;
else if(a[i]+a[left]+a[right]<target) left++;

步骤⑤(去重的处理)

本题涉及到两个去重,一个是i的去重,一个是指针的去重。

i的去重:例如当数组前有两个-3,那么第一个-3已经利用过一遍在记录答案上,那么在i走到第二个-3的时候,其实可以直接continue,进行下一个循坏!

for(int i=1;i<=n-2;i++)
	{
        //最外层i的去重操作
		if(i>1&&a[i]==a[i-1]) continue;

双指针的去重:假设i已经走到0处,我们最终的target值为0,那么数组刚好有0000的子串,例如

在这个时候,我们已经锁定了一个答案,那么在得到答案之后,开始让左指针和它的下一个数比较,让右指针和它的前一个数比较,如果是相同的,直接让指针收缩,最终相遇。在这里注意一下,在没找到一组答案之前,不要进行去重的操作,否则会漏掉000的答案,大家待会可以结合代码理解一下!

 //找到答案之后的双指针的去重操作
				    while(right>left&&a[right]==a[right-1]) right--;
			        while(right>left&&a[left]==a[left+1]) left++;
				    left++,right--;

五、引入的完整代码

#include<bits/stdc++.h>
using namespace std;
int a[100000];
int main()
{
	int n,target;
	cin>>n>>target;
    //数组下标从1开始的注意一下!!!!!!!!    
	for(int i=1;i<=n;i++) cin>>a[i];
	int sum=0;
    //sort排序从小到大
	sort(a+1,a+n+1);
    //最外层的i循坏
	for(int i=1;i<=n-2;i++)
	{
        //最外层i的去重操作
		if(i>1&&a[i]==a[i-1]) continue;
            //双指针的初始位置
			int left=i+1,right=n;
			while(left<right)
			{
				if(a[i]+a[left]+a[right]>target) right--;
				else if(a[i]+a[left]+a[right]<target) left++;
                //找到答案了
				else 
				{
					sum++;
                    //找到答案之后的双指针的去重操作
				    while(right>left&&a[right]==a[right-1]) right--;
			        while(right>left&&a[left]==a[left+1]) left++;
				    left++,right--;
				}
			}
	}
	cout<<sum<<endl;
}

 六、四数之和

在有了三数之和的基础上,大家可以想想四数之和怎么做(甚至推广到n数之和),其实都是一个做法,无非是外层循坏i里面,再次套一个内层for循坏j,然后让双指针从j的后面开始循坏,直接给大家上类似的代码,有不懂的地方可以评论区留言!

完整代码

#include<bits/stdc++.h>
using namespace std;
int a[100000];
int main()
{
	int n,target;
	cin>>n>>target;
	for(int i=1;i<=n;i++) cin>>a[i];
	int sum=0;
	sort(a+1,a+n+1);
	for(int i=1;i<=n-3;i++)
	{
		if(i>1&&a[i]==a[i-1]) continue;
		for(int j=i+1;j<=n-2;j++)
		{
			if(j>i+1&&a[j]==a[j-1]) continue;
			int left=j+1,right=n;
			while(left<right)
			{
				if(a[i]+a[j]+a[left]+a[right]>target) right--;
				else if(a[i]+a[j]+a[left]+a[right]<target) left++;
				else 
				{
					cout<<i<<" "<<j<<" "<<left<<" "<<right<<" "<<endl;
					cout<<a[i]<<" "<<a[j]<<" "<<a[left]<<" "<<a[right]<<" "<<endl;
					cout<<"********************************************"<<endl;
					sum++;
				    while(right>left&&a[right]==a[right-1]) right--;
			        while(right>left&&a[left]==a[left+1]) left++;
				    left++,right--;
				}
			}
			
		}
	}
	cout<<sum<<endl;
}

测试用例

七、力扣的四数之和题目

①题目描述

  ②完整代码

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        if (nums.size() < 4) {
            return result;
        }
        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 j=i+1;j<nums.size()-2;j++)
		{
			if(j>i+1&&nums[j]==nums[j-1]) continue;
			int left=j+1,right=nums.size()-1;
			while(left<right)
			{
				if((long)nums[i]+nums[j]+nums[left]+nums[right]>target) right--;
				else if((long)nums[i]+nums[j]+nums[left]+nums[right]<target) left++;
				else 
				{
                    result.push_back({nums[i],nums[j],nums[left],nums[right]});
				    while(right>left&&nums[right]==nums[right-1]) right--;
			        while(right>left&&nums[left]==nums[left+1]) left++;
				    left++,right--;
				}
			}
			
		}
	}
    return result;
    }
};

③AC凭证

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sheep.ice

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

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

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

打赏作者

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

抵扣说明:

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

余额充值