LeetCode 18 四数之和

文章详细描述了解决LeetCode第18题的方法,即在给定数组中找到四个整数,使它们的和等于目标值。主要思路是先对数组排序,然后使用双指针技术寻找三数之和,同时处理去重问题,确保结果中没有重复的四元组。代码实现中包含了排序、遍历、内存管理和去重的逻辑。
摘要由CSDN通过智能技术生成

LeetCode 18 四数之和

给你一个由n个整数组成的数组nums,和一个目标值target。请你找出并返回满足下述全部条件且不重复的四元组nums[a],nums[b],nums[c],nums[d] (若两个四元组一一对应,则认为两个四元组重复):

  • 0 <= a,b,c,d < n

  • a,b,c,和d互不相同

  • nums[a] + nums[b] + nums[c] + nums[d] == target

示例1:

输入: nums = [1,0,-1,0,-2,2], target = 0输出: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例2:

输入: nums = [2,2,2,2,2], target = 8输出: [[2,2,2,2]]

思路(排序 + 双指针)

对数组进行排序,对排序后数组,确定第一个数的值后,将问题变为求三数之和,而求三数和思路是先定好一个数,然后利用双指针来寻找目标值,将问题转换为两数和。

难点:去重

当我们找到一个符合要求的元组时,如果我们改变其中的一个数,如果改变后的数和原先是一样的时,得到的结果也有可能是一样的,如数组nums[2,2,2,2,2] target = 8 时,输出应只有一个[[2,2,2,2]],而若是不进行去重的话,输出则有很多相同。我们这里的去重思路是:

  当我们找到一个符和要求的元组时,对两个指针分别向中间遍历,直到碰到不同的数时停止遍历;另外就是对固定另外两个数进行去重,当我们当前数和之前处理的数相同时,我们则直接跳过这个数。

代码实现

//比较两个整形 a,b大小,返回 > 0 则 a大,反之
    int cmpInt(const void* a,const void* b)
    {
        return *(int *)a - *(int *)b;    
    }
    // nums:输入数组   numsSize:输入数组的长度   target:目标值   *returnSize:返回数组的长度   *returnColumnSizes返回数组的列数
    int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes)
    {
    	//对nums进行排序
    	qsort(nums, numsSize, sizeof(int), cmpInt);
    
    	//进行分配内存操作
    	int base = numsSize;
    	*returnSize = 0;
    	int** ret = malloc(sizeof(int *) * base);
    	*returnColumnSizes = malloc(sizeof(int) * base);
    
    	//对输入数组进行遍历
    	int i = 0, j = 0;
    	//注意这里边界值, i < numsSize - 3 因为需要找 i 后面的三位数,所以 i < numsSize - 3 也可写成 i < numsSize因为后面双指针遍历结束条件是left < right 当  i  = numsSize - 3时, j = numsSize - 2 left = j + 1 = numsSize - 1 而right = numsSize - 1 条件不成立则直接跳过,为了减少遍历次数写成i < numsSize - 3 下面 j < numsSize - 2同理
    	for(i = 0; i < numsSize - 3; i++) 
    	{
    		//去重,当前数和前一个数相同时,直接跳过这一次
    		if(i > 0 && nums[i] == nums[i - 1])
    			continue;
    		for(j = i + 1; j < numsSize - 2; j++)
    		{
    			if(j > i + 1 && nums[j] == nums[j - 1])
    					continue;
    			int left = j + 1;
    			int right = numsSize - 1;
    			while(left < right)
    			{
    				//注意nums中元素的取值范围 -10^9 <= nums[i] <= 10^9 用int的话,多个nums元素相加可能会有溢出的情况,所以此处采用long
    				long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
    				//当相等时,找到符合要求的四个元素
    				if(target == sum)
    				{
    								
    					//判断当前数组的长度是否和给定长度相等,相等的话需要对数组进行扩容,避免空间不足的情况
    					if(base == *returnSize)
    					{
    							base *= 2;
    							ret = realloc(ret,sizeof(int *) * base);
    							*returnColumnSizes = realloc(*returnColumnSizes,sizeof(int) * base);
    					}
    		      //分配空间
    			    ret[*returnSize] = malloc(sizeof(int) * 4);
    					//赋值
    					ret[*returnSize][0] = nums[i];
    					ret[*returnSize][1] = nums[j];
    					ret[*returnSize][2] = nums[left];
    					ret[*returnSize][3] = nums[right];
    					//因为是四数之和,所以一定是4列
    					(*returnColumnSizes)[*returnSize] = 4;
    					//添加完后数组长度加一,注意 *returnSize++ 和 (*returnSize)++的区别
    					//*returnSize++  是指针后移,而(*returnSize)++是值增加
    					(*returnSize)++;
    
    					//进行去重操作,当找到一组合适的元组之后,left、right分别向中间遍历找到不等于当前元素的数
    					while(left < right && nums[right] == nums[right - 1])
    							right--;
    					right--;
    					while(left < right && nums[left] == nums[left + 1])
    							left++;
    					left++;
    				}
    				else if(target < sum)
    				{
    						right--;
    				}
    				else
    				{
    						left++;
    				}
    			}
    		}
    	}
    	return ret;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值