Two Sum, Three Sum, Four Sum 问题的解法

  • Two Sum问题

先来看Two Sum问题
在这里插入图片描述
有许多种做法,我第一个想到的是用哈希表来做,在C++里也就是unordered_map,用它来存贮nums中的元素以及对应的下标。然后在遇到新的元素的时候进行查找时候存在index[ target-nums[i]]这个键,如果存在就找到了需要的元素。

 vector<int> twoSum(vector<int>& nums, int target) 
    {
        vector<int> ans;
        int len=nums.size();
        unordered_map<int,int> index;
        for(int i=0;i<len;i++)
        {
            if(index.find(target-nums[i])!=index.end())
            {
                ans.push_back(i);
                ans.push_back(index[target-nums[i]]);
            }
            index[nums[i]]=i;
        }
        return ans;
    }

后来在网上找到了更好的双指针解法,具体来说就是用两个指针low和high分别指向数组的头部和尾部,看指向的值的加和会不会等于target,如果比target小,则low++,如果比target大,则high–。要做到这一点,我们就得给数组排好序。从而保证low和high指向的始终是[low,high]区间的最小值和最大值。
代码如下:

vector<int> twoSum(vector<int>& nums, int target) {
    // 先对数组排序
    sort(nums.begin(), nums.end());
    // 左右指针
    int lo = 0, hi = nums.size() - 1;
    while (lo < hi) {
        int sum = nums[lo] + nums[hi];
        // 根据 sum 和 target 的比较,移动左右指针
        if (sum < target) {
            lo++;
        } else if (sum > target) {
            hi--;
        } else if (sum == target) {
            return {nums[lo], nums[hi]};
        }
    }
    return {};
}

- Three Sum问题
在这里插入图片描述在这里ThreeSum问题要求找出a+b+c=0条件成立的a,b,c。我们稍微转化一下思路,可以看成找出a+b=-c成立的a,b,c值。那么问题就很明朗了,我们只需要穷举c,然后调用twosum函数就可以解决。
先看代码

 vector<vector<int>> threeSum(vector<int>& nums) 
    {
        // 数组首先排个序
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> res;
        // 穷举 threeSum 的第一个数
        for (int i = 0; i < n; i++) 
        {
            // 对 target - nums[i] 计算 twoSum
            vector<vector<int>> 
                tuples = twoSumTarget(nums, i + 1, - nums[i]);
            // 如果存在满足条件的二元组,再加上 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>> twoSumTarget(vector<int>& nums, int start, int target) 
    {
        // 左指针改为从 start 开始,其他不变
        //为什么左边指针改为从start(也就是i+1)开始?
        //因为start之前的元素都是在穷举ThreeSum的第一个数的时候穷举过的,如果将这些元素再次加入作为第二个或第三个数字,那么就会造成重复。
        int lo = start; 
        int hi = nums.size() - 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是防止重复的值出现
                while (lo < hi && nums[lo] == left) lo++;
            } 
            else if (sum > target) 
            {
            	//这里while是防止重复的值出现
                while (lo < hi && nums[hi] == right) hi--;
            } 
            else 
            {
                res.push_back({left, right});
                //这里while是防止重复的值出现
                while (lo < hi && nums[lo] == left) lo++;
                while (lo < hi && nums[hi] == right) hi--;
            }
        }
        return res;
    }

我们这里需要注意的是解可能会重复出现,在求解的时候需要十分小心地考虑去重。

对于twosum函数来说,如果lo和hi指向的值之前已经出现过了,就需要跳过,否则若匹配成功就会给加入到结果集合res中。对于threesum函数来说,我们穷举nums[i]的时候也需要注意跳过后边重复出现的值。

同时,因为在穷举ThreeSum的第一个数的时候穷举过的nums[i],如果将这些元素再次加入twosum中作为第二个或第三个数字,那么就会造成重复。因此low指针必须从threesum穷举过的元素下标的下一个开始。

  • Four Sum问题
    在这里插入图片描述学会了ThreeSum问题,FourSum问题就很好解决了,其实就是之前的问题的套娃。代码如下:
vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        int n=nums.size();
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        for(int i=0;i<n;i++)
        {
            vector<vector<int>> tirples=
                threeSum(nums,i+1,target-nums[i]);
            for(vector<int>& tirple : tirples)
            {
                tirple.push_back(nums[i]);
                res.push_back(tirple);
            }
            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;
        // 穷举 threeSum 的第一个数
        for (int i = start; i < n; i++) 
        {
            // 对 target - nums[i] 计算 twoSum
            vector<vector<int>> 
                tuples = twoSumTarget(nums, i + 1, target- nums[i]);
            // 如果存在满足条件的二元组,再加上 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>> twoSumTarget(vector<int>& nums, int start, int target) 
    {
        // 左指针改为从 start 开始,其他不变
        //为什么左边指针改为从start(也就是i+1)开始?
        //因为start之前的元素都是在穷举ThreeSum的第一个数的时候穷举过的,如果将这些元素再次加入作为第二个或第三个数字,那么就会造成重复。
        int lo = start; 
        int hi = nums.size() - 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;
    }

参考文章:https://mp.weixin.qq.com/s?__biz=MzAxODQxMDM0Mw==&mid=2247485789&idx=1&sn=efc1167b85011c019e05d2c3db1039e6&scene=21#wechat_redirect

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值