Leetcode 18. 四数之和

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).
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法

思路一:暴力法

简单易懂枚举所有可能

代码
vector<vector<int>> fourSum(vector<int>& nums, int target) {
        if(nums.size()<4) return {};
        sort(nums.begin(),nums.end());
        set<vector<int>> a;//用来去重
        vector<vector<int>> res;
        for(int i=0;i<nums.size()-3;i++)
        {
            if(nums[i]>target&&target>0) break;
            for(int j=i+1;j<nums.size()-2;j++)
            {
                for(int l=j+1;l<nums.size()-1;l++)
                {
                    for(int r=l+1;r<nums.size();r++)
                    {
                        if(nums[i]+nums[j]+nums[l]+nums[r]==target)
                            a.insert(vector<int>{nums[i],nums[j],nums[l],nums[r]});
                    }
                }
            }
        }
        for(auto c:a)
        {
            res.push_back(c);
        }
        return res;
    }


作者:gpe3DBjDS1
来源:力扣(LeetCode)
链接
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路二:双指针

在 3Sum 的基础上再加一层循环即可,3Sum 时间复杂度为 O ( n 2 ) O(n2) O(n2),所以这个方法时间复杂度为 O ( n 3 ) O(n3) O(n3)

代码
 vector<vector<int>> fourSum(vector<int>& nums, int target) {
        if(nums.size()<4) return {};
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        set<vector<int>> a;//去重
        for(int i=0;i<nums.size()-3;i++)
        {
            if(nums[i]>target&&target>0) break;
            for(int j=i+1;j<nums.size()-2;j++)
            {
                int l=j+1;
                int r=nums.size()-1;
                while(l<r)
                {
                    if(nums[i]+nums[j]+nums[l]+nums[r]<target)
                        l++;
                    else if(nums[i]+nums[j]+nums[l]+nums[r]>target)
                        r--;
                    else 
                    {
                        vector<int> temp{nums[i],nums[j],nums[l],nums[r]};
                        a.insert(temp);
                        l++;
                        r--;
                    }
                }
            }
        }
        for(auto c:a)
        {
            res.push_back(c);
        }
        return res;
    }

作者:gpe3DBjDS1
来源:力扣(LeetCode)
链接
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路三:哈希表

排序
从左遍历,每次遍历进入3
从右遍历,每次进入4
设定左右索引,指向 2、3 还没遍历的数组框左右,判断索引数之和与temp_target,大了右索引左移,小了左索引右移,符合的数放入return。
这个没有写,如果还需要提高时间,那么这样想,因为是4个数之和,可以看成两组数之和,每组两个数,这样如果知道了每两个数之和,问题如上面的转换,这样时间便是O(n^2),不过空间就需要大很多。维持如下的结构:

在这里插入图片描述

维持这样的结构,第一行为组的和,然后指向所有的组合,因为c++ map 是会自动排序的,所以创建 map<int,pari<int,int> > > 这样的一个表便可以了,然后就是剩下判断问题了,如只有 -2 0 2 各一个,但是 -2 2 是可以的,所以需要考虑个数问题。

我用了unorder_map,并没有通过双向收缩来实现,所以判断起来比较麻烦,不过map 在初始化的时候,时间需要 l o g n logn logn,所以这样的总体时间是 O ( n 2 l o g n ) O(n^2logn) O(n2logn),这个也是 d i s c u s s discuss discuss 普遍时间。而使用unorder_map,我的时间是 O ( n 2 + n M a x l e n ( b u c k e t ) 2 ) O(n^2 + nMaxlen(bucket)^2) O(n2+nMaxlen(bucket)2),上图就是 M a x l e n ( b u c k e t ) = 3 Maxlen(bucket)=3 Maxlen(bucket)=3,在n较大是较优,注意是+号,毕竟比较难表达,应该会接近 O ( n 2 ) O(n^2) O(n2)

计算两数之和,放入mp 中
统计各数的个数,使用map<int,int> cnt 存储
遍历mp
判断 target- val1 是否在mp 中,否继续遍历,这个时间是 O ( 1 ) O(1) O(1)
存在的话,如果 v a l 1 &gt; v a l 2 , c o n t i n u e val1 &gt; val2,continue val1>val2continue,为了避免重复
遍历bucket1 中的组合
遍历bucket2 中的组合,如果 m a x ( g r o u p 1 ) &lt; = m i n ( g r o u p 2 ) max(group1)&lt;=min(group2) max(group1)<=min(group2)则进入下一步,这是为了避免重复,等号为了 0,0,0,0情况
通过cnt 判断数的个数够不够,够的放入return。
结束

代码
 vector<vector<int> > fourSum(vector<int> &num, int target) {
 34         sort(num.begin(),num.end());
 35         vector<vector<int> > ret;
 36         unordered_map<int,vector<pair<int,int> > > mp;
 37         unordered_map<int,int> cnt;
 38         for(unsigned int a=0;a<num.size();){
 39             for(unsigned int b=a+1;b<num.size();){
 40                 mp[num[a]+num[b]].push_back(pair<int,int> {num[a],num[b]});
 41                 b++;
 42                 while(b<num.size()&&num[b-1]==num[b])    b++;
 43             }
 44             a++;
 45             while(a<num.size()&&num[a-1]==num[a])    a++;
 46         }
 47         for(unsigned int a = 0;a<num.size();a++)
 48             cnt[num[a]]++;
 49 //        for(unordered_map<int,int>::iterator it=cnt.begin();it!=cnt.end();it++)
 50 //            cout<<it->first<<":"<<it->second<<endl;
 51 //        for(unordered_map<int,vector<pair<int,int> > >::iterator it1=mp.begin();it1!=mp.end();it1++){
 52 //            cout<<it1->first<<":"<<endl;
 53 //            for(int i=0;i<it1->second.size();i++)
 54 //                cout<<it1->second[i].first<<" "<<it1->second[i].second<<endl;
 55 //        }
 56 
 57         for(unordered_map<int,vector<pair<int,int> > >::iterator it1=mp.begin();it1!=mp.end();it1++){
 58 //            cout<<it1->first<<endl;
 59             unordered_map<int,vector<pair<int,int> > >::iterator it2=mp.find(target - it1->first);
 60             if(it2!=mp.end()){
 61 //                cout<<it1->first<<it2->first<<endl;
 62 //                cout<<it1->second.size()<<it2->second.size()<<endl;
 63                 if(it1->first > it2->first) continue;
 64                 for(int i=0;i<it1->second.size();i++){
 65                     for(int j=0;j<it2->second.size();j++){
 66                         int a = it1->second[i].first,b = it1->second[i].second,c = it2->second[j].first,d = it2->second[j].second;
 67                         if(max(a,b)<=min(c,d)){
 68                             bool flag = true;
 69                             cnt[a]--;
 70                             cnt[b]--;
 71                             cnt[c]--;
 72                             cnt[d]--;
 73                             if(cnt[a]<0||cnt[b]<0||cnt[c]<0||cnt[d]<0)  flag = false;
 74                             cnt[a]++;
 75                             cnt[b]++;
 76                             cnt[c]++;
 77                             cnt[d]++;
 78 //                            cout<<a<<" "<<b<<" "<<c<<" "<<d<<" "<<flag<<endl;
 79                             if(flag){
 80                                 vector<int> tmp = {a,b,c,d};
 81                                 sort(tmp.begin(),tmp.end());
 82                                 ret.push_back(tmp);
 83                             }
 84                         }
 85                     }
 86                 }
 87             }
 88         }
 89         return ret;
 90     }

思路四:递归

将k个数转化为k-1个数的问题,不断递归,直到两个数。

代码
class Solution {
    vector<vector<int> > kSum(vector<int>& nums, int start, int k, int target)
    {
        vector<vector<int> > res;
        int len=nums.size();
        if(k==2)
        {
            int left=start,right=len-1;
            int lastleftval=-1,lastrightval=-1;
            bool flag=false;// flag=false表示还未满足条件的元祖
            while(left<right)
            {
                if((nums[left]+nums[right])>target)
                    right--;
                else if((nums[left]+nums[right])<target)
                    left++;
                else
                {
                    if(flag==true && lastleftval==nums[left] && lastrightval==nums[right])
                    {
                        left++;
                        right--;
                        continue;
                    }
                    flag=true;
                    lastleftval=nums[left];
                    lastrightval=nums[right];
                    vector<int> subres;
                    subres.push_back(nums[left]);
                    subres.push_back(nums[right]);
                    res.push_back(subres);
                    left++;
                    right--;
                }
            }
            return res;
        }
        int lastval=-1;
        for(int i=start; i<len; i++)
        {
            if(i!=start && nums[i]==nums[i-1])
                continue;
            vector<vector<int> > subres= kSum(nums, i+1, k-1, target-nums[i]);
            int sublen=subres.size();
            for(int j=0; j<sublen; j++)
            {
                vector<int>temp;
                temp.push_back(nums[i]);
                for(int j1=0; j1<k-1; j1++)
                    temp.push_back(subres[j][j1]);
                res.push_back(temp);
            }
        }
        return res;
    }
public:
    vector<vector<int> > fourSum(vector<int>& nums, int target) {
        vector<vector<int> > res;
        int len=nums.size();
        if(len==0)
            return res;
        sort(nums.begin(),nums.end());
        return kSum(nums,0,4,target);
    }
};

来源:链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值