leetcode:四数之和(详解)

内容:题目,代码实现及注解,思路详解

题目:

给你一个由 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;
            }

//以下是在固定住前面两数之和,使用双指针法组合后面的两数之和,使得四数之和==target
            int 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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值