18. 4Sum

本文探讨了如何从数组中找出四个整数相加等于目标值的所有唯一组合,并提供了三种不同的算法实现方案,包括直接双指针法、使用哈希表进行缓存的方法以及优化的双指针法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

Given an array S of n integers, are there elements abc, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

Note:

  • Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
  • The solution set must not contain duplicate quadruplets.
题意:

从给定的数组中找到四个元素之和为target的组合,保存之后返回。要求a ≤ b ≤ c ≤ d,结果集中不包含有重复组合。

思路一:

先排序,然后左右夹逼,复杂度O(n3),会超时。
可以用一个hashmap 先缓存两个数的和,最终复杂度O(n3)。这个策略也适用于3Sum 。

代码:176ms

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        
        vector<vector<int>> result;
        auto last = nums.end();
        
        if(nums.size()<4){
            return result;
        }
        
        sort(nums.begin(), nums.end());
        
        for(auto a=nums.begin(); a<prev(last, 3); ++a){
            for(auto b=next(a); b<prev(last, 2); ++b){
                auto c = next(b);
                auto d = prev(last);
                while(c<d){
                    if(*a+*b+*c+*d<target){
                        ++c;
                    }else if(*a+*b+*c+*d>target){
                        --d;
                    }else{
                        result.push_back({*a, *b, *c, *d});
                        ++c;
                        --d;
                    }
                }
            }
        }
        
        sort(result.begin(), result.end());
        result.erase(unique(result.begin(), result.end()), result.end());
        return result;
    }
};
思路二:

map 做缓存, 用一个hashmap 先缓存两个数的和
// 时间复杂度,平均O(n^2),最坏O(n^4),空间复杂度O(n^2).

代码:236ms

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        
        vector<vector<int>> result;
        
        if(nums.size()<4){
            return result;
        }
        
        sort(nums.begin(), nums.end());
        
        unordered_map<int, vector<pair<int, int>>> cache;
        for(size_t a=0; a<nums.size(); ++a){
            for(size_t b=a+1; b<nums.size(); ++b){
                cache[nums[a]+nums[b]].push_back(pair<int, int>(a, b));
            }
        }
        
        for(int c=0; c<nums.size(); ++c){
            for(int d=c+1; d<nums.size(); ++d){
                const int key = target-nums[c]-nums[d];
                if(cache.find(key)==cache.end())
                    continue;
                
                const auto& vec = cache[key];
                for(size_t k=0; k<vec.size(); ++k){
                    if(c<=vec[k].second)
                        continue;
                        
                    result.push_back({nums[vec[k].first], nums[vec[k].second], nums[c], nums[d]});
                }
            }
        }
        
        sort(result.begin(), result.end());
        result.erase(unique(result.begin(), result.end()), result.end());
        
        return result;
    }
};
思路三:

同样适用的是夹逼方法,主要是排除了很多其他浪费时间的因素。最快的方法。

代码:16ms

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        
        vector<vector<int>> result;
        int n = nums.size();
        
        if(nums.size()<4){
            return result;
        }
        
        sort(nums.begin(), nums.end());
        
        for(int i=0; i<n-3; i++){
            if(i>0 && nums[i]==nums[i-1])
                continue;
            
            if((nums[i]+nums[i+1]+nums[i+2]+nums[i+3])>target)
                break;
                
            if((nums[i]+nums[n-3]+nums[n-2]+nums[n-1])<target)
                continue;
            
            for(int j=i+1; j<n-2; j++){
                if(j>i+1 && nums[j]==nums[j-1])
                    continue;
                
                if((nums[i]+nums[j]+nums[j+1]+nums[j+2])>target)
                    break;
                
                if((nums[i]+nums[j]+nums[n-2]+nums[n-1])<target)
                    continue;
                    
                int left = j+1;
                int right = n-1;
                while(left<right){
                    int sum = nums[left]+nums[right]+nums[i]+nums[j];
                    if(sum<target){
                        left++;
                    }else if(sum>target){
                        right--;
                    }else{
                        result.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
                        do{
                            left++;
                        }while(nums[left]==nums[left-1] && left<right);
                        
                        do{
                            right--;
                        }while(nums[right]==nums[right+1] && left<right);
                    }
                }
            }
        }
        
        return result;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值