内容:题目,代码实现及注解,思路详解
题目:
给你一个由 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]]
提示:
1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码实现:
int cmp(const void*e1,const void*e2){return *(int*)e1 - *(int*)e2;}int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes)
{*returnSize = 0;if(nums==NULL || numsSize<4){return NULL;}qsort(nums,numsSize,sizeof(int),cmp);//排序int**ret = (int**)malloc(sizeof(int*)*numsSize*numsSize);* returnColumnSizes = (int*)malloc(sizeof(int)*numsSize*numsSize);int i = 0;for(i=0;i<numsSize-3;i++)//枚举确定第一个数{if(i>0 && nums[i]==nums[i-1])//排除第一个数的重复项{continue;}if((long)nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target)//剪枝1:第一个数+最小的三数组合>target,则它及其之后所有枚举的第一个数都不符合要求,直接break,退出枚举循环{break;}if((long)nums[i]+nums[numsSize-1]+nums[numsSize-2]+nums[numsSize-3]<target)剪枝2:第一个数+最大的三数组合<target,则当前枚举的第一个数无论怎样与后面的三个数组合都不可能=target,跳过当前枚举的第一个数{continue;}int j = 0;for(j=i+1;j<numsSize-2;j++)//枚举确定第二个数{if(j>i+1 &&nums[j]==nums[j-1])//排除第二个数的重复项{continue;}if((long)nums[i]+nums[j]+nums[j+1]+nums[j+2]>target)//剪枝1:第一个数固定后,当前枚举的第二个数,再挑选最小的两数组合,四数之和若>target,则当前枚举的第二个数及其后面所有的枚举都不符合要求,直接break{break;}if((long)nums[i]+nums[j]+nums[numsSize-1]+nums[numsSize-2]<target)//剪枝2:第一个数固定后,当前枚举的第二个数,再挑选最大的两数组合,四数之和若<target,则当前枚举的第二个数,无论再怎么与后面的两数组合,四数之和都<target,则跳过当前枚举的第二个数{continue;}//以下是在固定住前面两数之和,使用双指针法组合后面的两数之和,使得四数之和==targetint left = j+1;int right = numsSize-1;while(left<right){long sum =(long) nums[i]+nums[j]+nums[left]+nums[right];if(sum==target){ret[*returnSize] = (int*)malloc(sizeof(int)*4);ret[*returnSize][0] = nums[i];ret[*returnSize][1] = nums[j];ret[*returnSize][2] = nums[left];ret[*returnSize][3] = nums[right];(* returnColumnSizes)[*returnSize] = 4;(*returnSize)++;
//排除与确定组合重复的所有项:while(left<right && nums[left]==nums[++left]);while(left<right && nums[right]==nums[--right]);}else if(sum<target){while(left<right && nums[left]==nums[++left]);//排除不符合要求的重复项}else if(sum>target){while(left<right && nums[right]==nums[--right]);//排除不符合要求的重复项}}}}return ret;
}
思路详解:
方法:双重循环枚举前两数的组合+双指针法寻求后两数之和
首先对数组进行升序排序
1 双重循环:
第一重循环枚举确定第一个数:
a 排除枚举第一个数出现的重复项,continue的作用是跳过本轮循环,进入下一轮循环
b 剪枝1:第一个数确定后,与最小的三数组合相加,结果>target,说明当前枚举的第一个数及其后面所有枚举的第一个数都不能用,因为是升序,那就break,退出第一个数的枚举循环
剪枝2:第一个数确定后,与最大的三数组合相加,结果<target,说明当前枚举的第一个数无论怎样与后面任意挑选的三个数组合,结果都不可能=target,又由于是升序,当前枚举的第一个数不行,那就换下一个枚举的第一个数,跳过本轮循环,进入下一轮循环,后面枚举的第一个数中可能存在符合要求的
第二重循环枚举确定第二个数:
a 排除枚举第二个数出现的重复项,continue的作用是跳过本轮循环,进入下一轮循环
b 剪枝1:第一个数固定后,当前枚举的第二个数,再与挑选的最小的两数相加,四数之和若>target,则说明当前枚举的第二个数与后面所有枚举的第二个数都不可用,直接break退出第二个数的枚举循环
剪枝2:第一个数固定后,当前枚举的第二个数,再与挑选的最大的两数相加,四数之和若<target
则说明当前的第一个数字与第二个数字无论怎样与后面的两数组合,都不可能满足要求,又由于是升序,则跳过本轮枚举的第二个数,进入下一轮枚举的第二个数 ,后面枚举的第二个数中可能存在符合要求的
2 双指针组合两数之和:
需要注意的是四数之和可能超过整型范围,所有需要对其中的任意一个数进行long类型的强制转换,这样就不会出现整型溢出的现象了
两数之和的详解我已经写过,不再赘述:http://t.csdn.cn/JNfQ7