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;
}