LeetCode刷题系列18

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

示例:

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]



来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
  • C++编程实现:
排序+双指针

和三数之和一样,本题的难点依旧在于如何去除重复解
取两个数组合,将问题转化为三数之和
算法流程:

    特判,对于数组长度n,如果数组为Null或者数组长度小于4,返回[]。
    对数组进行排序。
    遍历排序后数组:
        对于重复元素,跳过,条件:i>0且nums[i]==nums[i−1],避免出现重复解
        二次遍历,重复元素跳过,判断重复元素从i后第二个元素开始,所以条件:j−i>1且nums[j]==nums[j−1]
        令左指针L=j+1,右指针R=n−1,当L<R时,执行循环:
        *当nums[i]+nums[j]+nums[L]+nums[R]==target时,将结果加入res并执行循环,判断左界和右界是否和下一位置重复,以去除重复解。并同时将L,R移到下一位置,寻找新的解
        *若和大于0,说明nums[R]太大,R左移
        *若和小于0,说明nums[L]太小,L右移

剪枝条件:

对于本题,按照上述流程写下来,可以通过。
我们继续对算法进行剪枝优化
第一次遍历

    若nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target,则可以退出,因为最小四数之和大于目标,则不可能存在结果。**注意:**和三数之和的优化条件不同,三数之和中target=0,所以只要nums[i]>0,则可退出,这里则需要更为严格的条件。
    若当前值和数组中最大的三个值相加依旧小于目标,nums[i]+nums[n−1]+nums[n−2]+nums[n−3]<target,则continue

第二次遍历

    同理,若nums[i]+nums[j]+nums[j+1]+nums[j+2]>target,break
    nums[i]+nums[j]+nums[n−1]+nums[n−2]<target,continue

复杂度分析

    时间复杂度:O(n3),数组排序O(Nlog⁡N),两次遍历数组O(n2),双指针遍历O(n),总体O(Nlog⁡N)+O(n2)∗O(n),O(n3)
    空间复杂度:O(1)

作者:zhu_shi_fu
链接:https://leetcode-cn.com/problems/4sum/solution/gu-ding-tao-lu-jian-dan-qing-xi-pai-xu-shuang-zhi-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        int nsize = nums.size();
        if (nsize < 4)
            return {};
        sort(nums.begin(),nums.end());
        vector<vector<int>> retvec;
        int l, r;
        for (int i=0; i<nsize-3; i++)
        {
            if(nums[i]+nums[i+1]+nums[i+2]+nums[i+3] > target) break;
            if(nums[i]+nums[nsize-3]+nums[nsize-2]+nums[nsize-1] < target) continue;
            if(i>0 && nums[i]==nums[i-1]) continue;
            
            for (int j=i+1; j<nsize-2; j++)
            {
                if(nums[i]+nums[j]+nums[j+1]+nums[j+2] > target) break;
                if(nums[i]+nums[j]+nums[nsize-2]+nums[nsize-1] < target) continue;
                if(j>i+1 && nums[j]==nums[j-1]) continue;
                
                l = j+1;
                r = nsize-1;
                while (l<r)
                {
                    int sumn = nums[i] + nums[j] + nums[l] + nums[r];
                    if (sumn < target)
                        l++;
                    else if(sumn > target)
                        r--;
                    else
                    {
                        retvec.push_back({nums[i], nums[j], nums[l], nums[r]});
                        while(l<r && nums[l] == nums[++l]);
                        while(l<r && nums[r] == nums[--r]);
                    }
                }
            }
        }
        return retvec;
    }
};

重要的是剪枝条件要考虑周全,才能保证舍去不必要的循环。

发布了36 篇原创文章 · 获赞 23 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/yy2yy99/article/details/102934833