一个函数秒杀 2Sum 3Sum 4Sum 问题

1、2Sum

如果假设输入一个数组 nums 和一个目标和 target,请你返回 nums 中能够凑出 target 的两个元素的值,比如输入 nums = [5,3,1,6], target = 9,那么算法返回两个元素 [3,6]。nums 中可能有多对儿元素之和都等于 target,请你的算法返回所有和为 target 的元素对儿,其中不能出现重复。

解析:

可以先对 nums 排序,然后利用前文「双指针技巧汇总」写过的左右双指针技巧,从两端相向而行就行了。但关键难点是现在可能有多个和为 target 的数对儿,还不能重复,
出问题的地方在于 sum == target 条件的 if 分支,当给 res 加入一次结果后,lo 和 hi 不应该改变 1 的同时,还应该跳过所有重复的元素,这样就可以保证一个答案只被添加一次,重复的结果都会被跳过,可以得到正确的答案。不过,受这个思路的启发,其实前两个 if 分支也是可以做一点效率优化,跳过相同的元素。

vector<vector<int>> twoSumTarget(vector<int>& nums, int target) {
    // nums 数组必须有序
    sort(nums.begin(), nums.end());
    int lo = 0, hi = nums.size() - 1;
    vector<vector<int>> res;
    while (lo < hi) {
        int sum = nums[lo] + nums[hi];
        int left = nums[lo], right = nums[hi];
        // 根据 sum 和 target 的比较,移动左右指针
        if (sum < target) {
            while (lo < hi && nums[lo] == left) lo++;
        } else if (sum > target) {
            while (lo < hi && nums[hi] == right) hi--;
        } else {
            res.push_back({left, right});//出问题的地方在于 sum == target 条件的 if 分支,当给 res 加入一次结果后,lo 和 hi 不应该改变 1 的同时,还应该跳过所有重复的元素:
            while (lo < hi && nums[lo] == left) lo++;
            while (lo < hi && nums[hi] == right) hi--;
        }
    }
    return res;
}

这个函数的时间复杂度非常容易看出来,双指针操作的部分虽然有那么多 while 循环,但是时间复杂度还是 O(N),而排序的时间复杂度是 O(NlogN),所以这个函数的时间复杂度是 O(NlogN)。

2、3Sum

力扣15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c
= 0 ?请你找出所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。

示例 1: 输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 示例 2: 输入:nums
= [] 输出:[] 示例 3: 输入:nums = [0] 输出:[]

解析:

本题是三数之和,借助于二数之和的模板,通过遍历第一个数i,然后调用二数之和函数,从i+1开始,二数之和的target=三数之和的target(0)-nums[i],

代码:
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int n=nums.size();
        vector<vector<int>> res;
        for(int i=0;i<n;i++){
            vector<vector<int>> tuples=twoSum(nums,i+1,0-nums[i]);
            for(vector<int>& tuple:tuples){
                tuple.push_back(nums[i]);
                res.push_back(tuple);
            }
        while(i<n-1 && nums[i]==nums[i+1]) i++;// 跳过第一个数字重复的情况,否则会出现重复结果
        }
        return res;
        
    }

    vector<vector<int>> twoSum(vector<int>& nums,int start,int target){
        int n=nums.size();
        int lo=start,hi=n-1;
        vector<vector<int>> res;
        while(lo<hi){
            int sum=nums[lo]+nums[hi];
            int left=nums[lo],right=nums[hi];
            if(sum<target){
              while(lo<hi && nums[lo]==left)  lo++;
            }else if(sum>target){
              while(lo<hi && nums[hi]==right)  hi--;
            }else{
                res.push_back({left,right});
                while(lo<hi && nums[lo]==left)  lo++;
                while(lo<hi && nums[hi]==right)  hi--;
            }
        }
       return res; 
    }
};

4、4Sum问题

力扣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]]

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

nSum 本题借助三数之和思想,遍历第一个数,然后调用三数之和的函数,得到后三个元组,然后这四个数相加之和为target

代码
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        int n=nums.size();
        vector<vector<int>> res;
        for(int i=0;i<n;i++){
            vector<vector<int>> triples=threeSum(nums,i+1,target-nums[i]);
            for(vector<int>& triple:triples){
                triple.push_back(nums[i]);
                res.push_back(triple);
            }
            while(i<n-1 && nums[i]==nums[i+1]) i++;
        }
        return res;
    }
    vector<vector<int>> threeSum(vector<int>& nums,int start,int target){
        int n=nums.size();
        vector<vector<int>> res;
        for(int i=start;i<n;i++){
            vector<vector<int>> tuiples=twoSum(nums,i+1,target-nums[i]);
            for(vector<int>& tuiple:tuiples){
                tuiple.push_back(nums[i]);
                res.push_back(tuiple);
            }
            while(i<n-1 && nums[i]==nums[i+1]) i++;
        }
        return res;
    }
    vector<vector<int>> twoSum(vector<int>& nums,int start,int target){
        int n=nums.size();
        int lo=start,hi=n-1;
        vector<vector<int>> res;
        while(lo<hi){
            int sum=nums[lo]+nums[hi];
            int left=nums[lo],right=nums[hi];
            if(sum<target){
                while(lo<hi && nums[lo]==left) lo++;
            }else if(sum>target){
                while(lo<hi && nums[hi]==right) hi--;
            }else{
                res.push_back({left,right});
                while(lo<hi && nums[lo]==left) lo++;
                while(lo<hi && nums[hi]==right) hi--;
            }
        }
        return res;
    }
};

5、nSum问题

总结上面的
/* 注意:调用这个函数之前一定要先给 nums 排序 */
vector<vector<int>> nSumTarget(
    vector<int>& nums, int n, int start, int target) {

    int sz = nums.size();
    vector<vector<int>> res;
    // 至少是 2Sum,且数组大小不应该小于 n
    if (n < 2 || sz < n) return res;
    // 2Sum 是 base case
    if (n == 2) {
        // 双指针那一套操作
        int lo = start, hi = sz - 1;
        while (lo < hi) {
            int sum = nums[lo] + nums[hi];
            int left = nums[lo], right = nums[hi];
            if (sum < target) {
                while (lo < hi && nums[lo] == left) lo++;
            } else if (sum > target) {
                while (lo < hi && nums[hi] == right) hi--;
            } else {
                res.push_back({left, right});
                while (lo < hi && nums[lo] == left) lo++;
                while (lo < hi && nums[hi] == right) hi--;
            }
        }
    } else {
        // n > 2 时,递归计算 (n-1)Sum 的结果
        for (int i = start; i < sz; i++) {
            vector<vector<int>> 
                sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
            for (vector<int>& arr : sub) {
                // (n-1)Sum 加上 nums[i] 就是 nSum
                arr.push_back(nums[i]);
                res.push_back(arr);
            }
            while (i < sz - 1 && nums[i] == nums[i + 1]) i++;
        }
    }
    return res;
}
例如上面4Sum问题套用模板
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        int n=nums.size();
        vector<vector<int>> res;
        for(int i=0;i<n;i++){
            vector<vector<int>> triples=nsum(nums,3,i+1,target-nums[i]);
            for(vector<int>& triple:triples){
                triple.push_back(nums[i]);
                res.push_back(triple);
            }
            while(i<n-1 && nums[i]==nums[i+1]) i++;
        }
        return res;
    }
    vector<vector<int>> nsum(vector<int>& nums,int n,int start,int target){
        int sz=nums.size();
        vector<vector<int>> res;
        if(n<2 || sz<n) return res;
        if(n==2){
            int lo=start,hi=sz-1;
            while(lo<hi){
                int sum=nums[lo]+nums[hi];
                int left=nums[lo], right=nums[hi];
                if(sum<target){
                    while(lo<hi && nums[lo]==left) lo++;
                }else if(sum>target){
                    while(lo<hi && nums[hi]==right) hi--;
                }else{
                    res.push_back({left,right});
                    while(lo<hi && nums[lo]==left) lo++;
                    while(lo<hi && nums[hi]==right) hi--;
                }
                
            }
        }
        else{
            for(int i=start;i<sz;i++){
                vector<vector<int>> sub=nsum(nums,n-1,i+1,target-nums[i]);
                for(vector<int>& arr:sub){
                    arr.push_back(nums[i]);
                    res.push_back(arr);
                }
                while(i<sz-1 && nums[i]==nums[i-1]) i++;
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值