四数之和的两种解题思路,C++

算法——四数之和

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

这一类题目包括两数之和、三数之和、四数之和、还可以拓展到K数之和。解题时可以从两数之和以及三数之和的求解中拓展开。
解体思路:

  1. 排序+双指针(适当剪枝可提高运行速度)

两数之和:
对数组nums进行升序排序,然后用两个指针i和j分别指向数组的头和尾,指针i和j向彼此靠拢,并判断nums[i]+nums[j]是否等于target。
三数之和:
在两数的基础上多了一个外层循环,在最外层循环中固定一个数(坐标为m),在内层循环中采用双指针指向另外两个数,判断nums[m]+nums[i]+nums[j]是否等于target。
四数之和:
在三数的基础上又多加一个外层循环,两个外层循环可固定两个数,内层双指针指向剩下的两个数。

//两数之和
vector> twoSum(vector& nums, int target) {
        vector> res;
        if(nums.size()<2) return res;
        sort(nums.begin(),nums.end());
        int i=0,j=nums.size()-1;
        while(i{nums[i],nums[j]});
                while(i> threeSum(vector& nums) {
        vector> res;
        if(nums.size()<3) return res;
        sort(nums.begin(),nums.end());
        for(int i=0;i0) break;
            if(i>0&&nums[i]==nums[i-1]) continue;
            int a=i+1,b=nums.size()-1;
            while(a{nums[i],nums[a],nums[b]});
                    while(a> foursum(vector& nums, int target){
        vector> res;
        if(nums.size()<4) return res;
        bool isbreak=false;
        sort(nums.begin(),nums.end());
        for(int i=0;i0&&nums[i]==nums[i-1]) continue;
            for(int j=i+1;jtarget){
                    isbreak=true;
                    break;
                }
                */
                int a=j+1,b=nums.size()-1;
                //剪枝法2:在每一轮通过四个数和的最大值和最小值加以限制
                if(nums[i]+nums[j]+nums[j+1]+nums[j+2]>target){
                    break;
                }
                if(nums[i]+nums[j]+nums[b]+nums[b-1]i+1&&nums[j]==nums[j-1]) continue;
                while(a{nums[i],nums[j],nums[a],nums[b]});
                        while(a
  1. 借助哈希表(字典)

两数之和:
遍历数组内的每一个元素nums[i],由元素为键,元素的坐标为值,将他们存入字典中(dict[nums[i]]=i),再次遍历数组nums,对于每个元素nums[j],只需要判断 target-nums[j] 是否在字典中,如果在且结果列表中还没有[nums[i],nums[j]],则将[nums[i],nums[j]]加入结果列表中。
三数之和:
类似于两数之和的思想,不过字典中的键是target-nums[i]-nums[j],对应的值是这两个数构成的组合,即pair(nums[i],nums[j])。
四数之和:
将四个数加法拆解成2个2个数的加法,那么它可以用类似两数相加的方法完成。在字典中,数组中的任意两数之和作为键,对应的值是一个集合,里面包含所有和等于键的两个数的坐标构成的组合。

代码比较好理解:

//两数之和
vector> twoSum(vector& nums, int target) {
        int n=nums.size();
        vector> res;
        if(n>=2)
        {
            unordered_map map1(n);
            for(int i=0;i{nums[j],nums[map1[key]]});
                    map1.erase(nums[j]); //将已经找到的元素对排除
                    map1.erase(key);
                }
            }
        }
        return res;
    }
 //三数之和  
vector> threeSum(vector& nums) {
        if(nums.size()<3) return vector>();
        vector> result;
        unordered_map> res;
        for(int i=0;i{nums[j],res[nums[j]].first,res[nums[j]].second});
                    res.erase(nums[j]);
                }
                else
                {
                    int key=0-nums[i]-nums[j];
                    res[key]=make_pair(nums[i],nums[j]);
                }
            }
        }
        return result;
    }

//四数之和,这部分写得不好,还请路过的大佬指点
vector> foursum(vector& nums, int target){
        vector> res;
        if(nums.size()<4) return res;
        sort(nums.begin(),nums.end());//排序一下方便后面去重
        unordered_map>> dict;
        for(int i=0;i0&&nums[i]==nums[i-1]) continue;
            for(int j=i+1;ji+1&&nums[j]==nums[j-1]) continue;
                int key=nums[i]+nums[j];
                dict[key].emplace(make_pair(i,j));
            }
        }            
        for(int i=0;i0&&nums[i]==nums[i-1]) continue;
            for(int j=i+1;ji+1&&nums[j]==nums[j-1]) continue;
                int key=target-nums[i]-nums[j];
                if(dict.find(key)!=dict.end()){
                //遍历集合中所有的两数对
                    for(auto kp:dict[key]){
                        if(kp.first>j){
                            //排序是为了对结果序列去重
                            vectortemp={nums[i],nums[j],nums[kp.first],nums[kp.second]};
                            sort(temp.begin(),temp.end());
                            bool rep=false;
                            if(!res.empty()){
                            for(vector& its:res){
                                	if(its[0]==temp[0]&&its[1]==temp[1]&&its[2]==temp[2]&&its[3]==temp[3]){rep=true;break;}
                                }
                            }
                            if(!rep) res.emplace_back(temp);
                        }
                    }
                }
            }
        }
        return res;
    }

你可能感兴趣的:(刷题啊,算法,c++)