三数之和系列题

1. LeetCode第18题—二数之和

在这里插入图片描述

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
      for(int i = 0;i<nums.size();++i)
      {
          for(int j = i+1;j<nums.size();++j)
          {
              if(nums[i]+nums[j] == target)
                return {i,j};
          }
      }  
      return {};
    }
};

1.1 LeetCode第560题—和为k的子数组

在这里插入图片描述
暴力解法:你会发现是通过不了的,因为时间复杂度太高,这道题使用的是前缀和 + 哈希表的思想,降低时间复杂度。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        //这道题所构建的unordered_map是非常有意思的
        //前缀和 + 哈希表
        unordered_map<int,int> hashmap; //前缀和与对应的个数
        hashmap[0] = 1; //前缀和为0的个数是1个
        int prevsum = 0;
        int count = 0;
        for(auto& e: nums)
        {
            prevsum += e;
            if(hashmap.find(prevsum-k) != hashmap.end())
                count += hashmap[prevsum -k];
            hashmap[prevsum]++;
        }
        return count;
    }
};

2. LeetCode第15题—三数之和

在这里插入图片描述
在这里插入图片描述
思路简化:

  1. 首先需要判断,如果数组元素都不满足3个,也就没有必要继续进行下去了,然后就是需要排序(时间复杂度为O(NlogN))
  2. 利用一个for循环确定一个k(第一次循环k不会重复,但是从第二次以后是由可能会重复的),然后选择双指针来查找我们需要相加为0的结果
  3. 但是在查找的过程有可能会出现重复的结果,所以需要去重
  4. sum = nums[i] + nums[left] + nums[right] < 0 说明左边的这个left值太小了,让整体偏小了,需要left++
  5. sum = nums[i] + nums[left] + nums[right] > 0 说明右边的这个right值太大了,让整体偏大了,需要right–;
  6. 当sum = nums[i] + nums[left] + nums[right] = 0 说明此时的结果就是我们需要的,可以直接的添加到我们需要返回的数组中,vector本身是支持"{}"花括号来进行初始化的,不要忘记了
  7. 需要注意的是确定k循环时间复杂度是O(N),双指针进行遍历的时间复杂度是O(N),所以总的时间复杂度为O(NlogN) + O(N) * O(N) ~ O(N*N)
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        if(n < 3)
            return {};
        //此时应该首先排除掉不可能的情况
        std::sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        for(int i = 0;i<n;++i)
        {
            if(nums[i] > 0)
                return ret;
            //我们发现我们对于i还没有去重
            if(i > 0 && nums[i] == nums[i-1])
                continue;
            int left = i+1;
            int right = n-1;
            while(left < right)
            {
                if(nums[i] + nums[left] + nums[right] < 0)
                {
                    //此时说明负数太大了,需要增加
                    left++;
                }
                else if(nums[i] + nums[left] + nums[right] > 0)
                {
                    //说明此时正数太大了,需要缩小
                    right--;
                }
                else
                {
                    //最后一种情况就是刚好等于0的时候了
                    ret.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    left++;
                    right--;
                    //到这里以后还需要挪动left和right,但是要考虑到去重
                    while(left < right && nums[left] == nums[left-1])
                        left++;
                    while(left < right && nums[right] == nums[right+1])
                        right--;
                }
            }
        }
        return ret;
    }
};

3. LeetCode第18题—四数之和

在这里插入图片描述
解题思路:其实本质上和三数之和是差不多的但是此时需要两个for循环来首先确定好两个数,剩下的就和三数之和的过程基本保持一致了,但是这里面有一个点是有可能出现错误的,那就是有可能大数溢出,那么全部相加sum = nums[i] + nums[j] + nums[left] + nums[right]的本身是有可能超过int的最大值的,所以这里采用相减的方式,避免都加在一起的情况,那么就能很好的避免大数溢出的情况了。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        //其实本质上和三数之和是差不多的
        int size = nums.size();
        if(size < 4)
            return {};
        std::sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        for(int i = 0;i<size;++i)
        {
    //这一层循环为的就是确定第一个数的位置,但是有可能是会右重复的,所以我们再这里需要考虑到去重的问题
            if(i > 0 && nums[i] == nums[i-1])
                continue;
            for(int j = i+1;j<size;++j)
            {
                //这一层就是为了能够确定第二个数,但是也要考虑到去重的问题
                if(j > i+1 && nums[j] == nums[j-1])
                    continue;
                //剩下的就和三数之和基本一直了
                int left = j+1;
                int right = size-1;
                while(left < right)
                {
                    //当全部的数相加起来太大的时候是可以选择,相减的方法来避免的大数溢出的
                    if(nums[i] + nums[j] - target < -(nums[left] + nums[right]))
                    {
                        left++;
                    }
                    else if(nums[i] + nums[j] - target > -(nums[left] + nums[right]))
                    {
                        right--;
                    }
                    else
                    {
                        //此时刚刚好,可以添加到我们需要的数组中了
                        ret.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
                        left++;
                        right--;
                        while(left<right && nums[left] == nums[left-1])
                            left++;
                        while(left < right && nums[right] == nums[right+1])
                            right--;
                    }
                }
            }
        }
        return ret;
    }
};

4. LeetCode第16题—最接近的三数之和

在这里插入图片描述
解题思路:
在这里插入图片描述
简单说:

  1. 对于这题来说基本上和三数之和思想保持一致,但是此时我们需要一个最小差距这么一个变量,需要不断的去更新这个数值,知道全部都遍历完的时候最终返回的只是一个整数。
  2. 我们需要关心的就是如果sum = nums[i] + nums[start] + nums[end] - targrt < ret - target,那么说明此时的这三个数之和的sum值更接近我们的target,需要进行更新
  3. 但是大于的情况我们是压根就不在乎的,sum <targrt的时候,说明start这个下标的值太小了,影响了整体偏小,需要跳过并且去重
  4. 当然sum > target情况是相同的,需要跳过right这个值,并且去重
class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        //这道题的核心就在于不断的更新那个你认为最接近的数,所以循环当中支队哪一个做判断,其他的少想点
        int ret = nums[0] + nums[1] + nums[2];
        std::sort(nums.begin(),nums.end());
        for(int i = 0;i<nums.size();++i)
        {
            if(i > 0 && nums[i] == nums[i-1])
                continue;
            int start = i + 1;
            int end = nums.size() - 1;
            while(start < end)
            {
                int sum = nums[i] + nums[start] + nums[end];
                //不要做无用功才是最好的代码
                if(abs(sum-target) < abs(ret-target))
                {
                    //说明这个sum才是我们想要的
                    ret = sum;
                }
                if(sum < target)
                {
                    start++;
                    //同时这里应该要考虑到去重的问题
                    while(start < end && nums[start] == nums[start-1])
                        start++;
                }
                else
                {
                    end--;
                    while(start < end && nums[end] ==nums[end+1])
                        end--;
                }
            } 
        }
        return ret;
    }
};

5. LeetCode第454题—四数相加II

LeetCode题目链接:https://leetcode-cn.com/problems/4sum-ii/
在这里插入图片描述
解题思路:
在这里插入图片描述
两个一组可以降低时间复杂度的计算。

class Solution {
public:
    //A + B + C + D = 0
    //A + B = -(C + D);
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        int res = 0;
        unordered_map<int,int> hashmap;
        for(int i = 0;i<nums1.size();++i)
        {
            for(int j = 0;j<nums2.size();++j)
            {
                int sunmAB = nums1[i]+nums2[j];
                hashmap[sunmAB]++;
            }
        }

        for(int i = 0;i<nums3.size();++i)
        {
            for(int j = 0;j<nums4.size();++j)
            {
                int sumCD = -(nums3[i]+nums4[j]);
                if(hashmap.find(sumCD) != hashmap.end())
                    res += hashmap[sumCD];
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值