两数之和系列

76 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情

两数之和系列

  • 针对的都是有序数组,如果给定的数组是无序的,那么先排序。
  • 两数之和系类都是双指针的使用,一个指向开头,一个指向结尾。
  • 根据 nums[L] + nums[R]target 的大小来判断是 ++L 还是 --R

两数之和II

只有一对答案,因此只需要一前一后靠近。 数组已经排序这点很重要

  • nums[L] + nums[R] < target :小于target++L 来增大区间和
  • nums[L] + nums[R] > target :大于target--R 减少区间和
  • nums[L] + nums[R] == target :可以直接得到结果返回
   class Solution {
   public:
       std::vector<int> twoSum(const std::vector<int>& nums, int target) {
         int L=0, R =nums.size()-1;
         std::vector<int> result(2);
 ​
         while(L < R) { 
           
           if(nums[L] + nums[R] == target) 
           { 
             result[0] = L+1;
             result[1] = R+1;
             return result;
           }
           else if(nums[L] + nums[R] < target) 
           { 
             ++L;
           }
           else 
           {
             --R;
           }
         }
 ​
         return result;
       }
   };

最接近的三数之和

对于nums中每个数,从其位置右边开始选出来两个数,使得三数之和最接近target。于是使用三个指针:curL=cur+1R=length-1。需要先对数组进行排序,根据三个指针的和,来调整指针位置。

  • 如果 sum = nums[cur] + nums[L] +nums[R] > target,需要 --R 来缩小,
  • 如果 sum = nums[cur] + nums[L] +nums[R] < target,需要 ++L 来扩大,
  • 如果 sum == target 找到答案,直接返回。
   class Solution {
   public:
       int threeSumClosest(std::vector<int>& nums, int target) {
         std::sort(nums.begin(), nums.end());
 ​
         int64_t result = INT_MAX;
 ​
         for(int cur=0, length = nums.size(); cur < length; ++cur) { 
           for(int L =cur+1, R =length-1; L < R;) { 
             int64_t sum = nums[cur] + nums[L] + nums[R];
             if(std::abs(target - sum) < std::abs(target - result))
             {
               result = sum;
             }  
 ​
             // 更新指针
             if(sum < target) 
             {
                 ++L;
             }
             else if(sum > target) 
             { 
                 --R;
             }
             else 
             { 
                 // sum == target
                 return static_cast<int>(result);
             }
           }  // inside-for -end
         }  // outside-for -end
 ​
         return static_cast<int>(result);
       }
   };

这个问题的解法思想,类似于盛水最多的容器,或者说是一种滑动窗口的思想:移动左指针变大,移动右指针变小。

三数之和

这道题和上一道几乎异曲同工。只是这道题是找出所有不重复的答案,因此在每次找到答案后需要剔除重复的元素。原理和上题一模一样。

   class Solution {
   public:
       std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
         std::vector<std::vector<int>> result;
         if(nums.size() <3) 
           return result;
 ​
         std::sort(nums.begin(), nums.end());
         
         for(int curr =0, numsLen = nums.size(); curr < numsLen; ) { 
 ​
           for(int L=curr+1, R =numsLen-1; L <R; ) { 
             
             int sum = nums[curr] + nums[L] + nums[R];
             if(sum < 0) 
             { 
               ++L;
             }
             else if(sum >0) 
             { 
               --R;
             }
             else 
             { 
               result.emplace_back<std::vector<int>>({nums[curr], nums[L], nums[R]});
               ++L;
               while(L < R && nums[L-1] == nums[L]) ++L; // 为了剔除重复元素
               --R;
               while(L < R && nums[R] == nums[R+1]) --R; // 为了剔除重复元素
             }
           }
 ​
           ++curr;
           while(curr < numsLen && nums[curr-1] == nums[curr]) ++curr; // 为了剔除重复元素   
         }
 ​
         return result;
       }
   };

四数之和

最直接的想法就是在 三数之和 外面再套一层循环。

   class Solution {
   public:
       vector<vector<int>> fourSum(vector<int>& nums, int target) {  
         std:vector<std::vector<int>> result;
         if(nums.size() < 4) 
           return result;
         
         std::sort(nums.begin(), nums.end());
 ​
         for(int curr=0, numsLen =nums.size(); curr < numsLen; ) { 
           M_threeSum(nums, curr, target-nums[curr], result);
 ​
           ++curr;
           while(curr < numsLen && nums[curr-1] == nums[curr]) ++curr; // 为了剔除重复元素   
         }
 ​
         return result;
       }
 ​
   private:
       void M_threeSum(const std::vector<int>& nums, 
                       int start, 
                       int target, 
                       std::vector<std::vector<int>>& result) {
         
         for(int curr =start+1, numsLen = nums.size(); curr < numsLen; ) { 
 ​
           for(int L=curr+1, R =numsLen-1; L <R; ) { 
             
             int sum = nums[curr] + nums[L] + nums[R];
             if(sum < target) 
             { 
               ++L;
             }
             else if(sum > target) 
             { 
               --R;
             }
             else 
             { 
               result.emplace_back<std::vector<int>>({nums[start], 
                                                      nums[curr], 
                                                      nums[L], 
                                                      nums[R]});
               ++L;
               while(L < R && nums[L-1] == nums[L]) ++L; // 为了剔除重复元素
               --R;
               while(L < R && nums[R] == nums[R+1]) --R; // 为了剔除重复元素
             }
           }
 ​
           ++curr;
           while(curr < numsLen && nums[curr-1] == nums[curr]) ++curr; // 为了剔除重复元素   
         }
       }
   };

至此,两数之和系列已经全数解决,都是一个双指针模板。