从 两数之和 — 四数之和(LeetCode)

本文详细解析了从两数之和到四数之和的算法思路,通过固定元素结合双指针技巧,实现高效查找目标和。涵盖排序、去重及复杂度优化等关键点,附带C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

N数之和==target的思路关键点有以下几个:

 

1.先排序,固定一个元素,利用指针移动其余元素

两数则固定一个元素+指针;

三数则固定两个元素+双指针;

四数则固定一个元素+三数和。

 

2.比较当前和与target的大小,移动指针。

因为提前将数组排序过,所以若当前和>target,则l--;

若当前和<target,则r++;

若当前和==target,则return或保存。

 

3.如果题目要求不重复,则去掉重复元素。

数组中元素可以重复,但是不能反回相同的答案,如[1,1,1,2],target=3;可以反回{[1,1,1] [1,2]},但不能多次返回[1,1,1] 以及 [1,2]。这时去掉首元素的重复元素: 

while(i>0 && nums[i]==nums[i-1])i++;
//或者
if(i>0 && nums[i]==nums[i-1])continue;

同时如果找到了和==target的N元素,那么也需要去重,即:N个数据需要去重N次,固定的数用首元素去重的方法,双指针指向的数在找到和==target之后去重:

while(i>0 && nums[i]==nums[i-1])i++;
//或者
if(i>0 && nums[i]==nums[i-1])continue;
//以三数为例
while(l<r && nums[l]==nums[l+1])l++;
while(l<r && nums[r]==nums[r-1])r--;

 

 

例题:

1. 两数之和

两数之和这个题目,如果要求返回的是元素不是下标,那么可以用指针来解。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n=nums.size();
        sort(nums.begin(),nums.end());
        int l=0,r=n-1;
        while(l<r){
            int sum=nums[l]+nums[r];
          if(sum<target){
              l++;
              
        }
        else if(sum>target){
            r--;
        }else return {l,r};

    }
            return {};
    }
};

 

2.三数之和

三数之和且不包含重复元素,那么可以固定一个数,另外两个利用当前和与target的大小移动双指针确定,同时在首元素和中间元素都去重。这道题确定了target=0。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
     int l=0,r=0,sum=0;
     vector<vector<int>> result;
     sort(nums.begin(),nums.end());
     for(int i=0;i<int(nums.size()-2);i++){
     
        if(i>0 && nums[i]==nums[i-1] ) continue;
         l=i+1;
         r=nums.size()-1;
         
         while(l<r && nums[r]>=0){
         sum=nums[i]+nums[l]+nums[r];
         if(sum==0 ){
         result.push_back({nums[i],nums[l],nums[r]});
             while(nums[r]==nums[r-1] && l<r) r--;
             while(nums[l]==nums[l+1] && l<r)l++;
             r--;
             l++;
         }else if(sum<0)l++;
         else r--;         
        }
     }
     return result;
    }
};

 

3.四数之和

四数之和就是固定两个数,再使用双指针,多了一层循环嵌套。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        
         sort(nums.begin(),nums.end());
         vector<vector<int>> res;
         

         for(int i=0;i+3<nums.size();i++){
             if(i>0 && nums[i]==nums[i-1])continue;
             
             for(int j=i+1;j+2<nums.size();j++){

                 if(j>i+1 && nums[j]==nums[j-1])continue;
                 int l=j+1,r=nums.size()-1;
                 int ttarget = target - nums[i] - nums[j] ;
                 while(l<r){
                 int sum=nums[l]+nums[r];
                  if(sum>ttarget)r--;
                  else if(sum<ttarget)l++;
                  else {
                      res.push_back({nums[i],nums[j],nums[l],nums[r]});
                  
                        while(l < r && nums[l] == nums[l+1])    l++;
                        while(l < r && nums[r] == nums[r-1])    r--;
                  
                      l++;
                      r--;   
                  }
               }
             }
         }
         return res;
    }
};

注意小细节:

1.for(int i=0;i+3<nums.size();i++)而不是for(int i=0;i<nums.size()-3;i++),因为有可能出现nums中元素个数小于三,在这里浪费我近半个小时……

2.注意先去重,后再l++,r--;

3.去中间重复项是在找到和==target之和,而不是在之前,因为有可能出现一开头的[1,1,1]情况,元素可以重复,不能重复的是数组,在找到后去重也是保证了这个效果;

4.去重后还需要i++,l--,因为此时的i、j是重复的最后一项。

扩展:

4.最接近的三数之和

和==target很接近,只需要多一个步骤,先计算差,再计算下一步。

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
      int n=nums.size(),res=nums[0]+nums[1]+nums[2];
      sort(nums.begin(),nums.end());
      for(int i=0;i<n-2;i++){
          int l=i+1,r=n-1;
          while(l<r){
              int ans=nums[i]+nums[l]+nums[r];
              if(abs(ans-target)<abs(res-target)){
                  res=ans;
              }
               if(ans>target){
                 r--;
               }
               else if(ans<target){
                 l++;
               }
               else return res;
          }
      }
      return res;
    }
};

 

5.三数之和的多种可能

核心还是固定一个数+双指针的移动,但是这里需要注意几个小细节,非常非常重要:

1)题目中没有说去掉重复元素,但是不去重直接计算会超时(不要问我怎么知道的),所以需要计算有几个重复的中间元素,相乘即可;

2)余数的可加性:a与b的和除以c的余数,等于a,b分别除以c的余数之和(或这个和除以c的余数)。所以可以先求余数再计算相加;

3)需要注意的是sum的位置,因为l和r指针是不断变化的,所以他一定是在while循环内;

4)注意此时while的条件,不再是l<r,而是A[l]<A[r],因为有元素相等的情况,并且元素相等情况特殊,需要单独讨论。

class Solution {
public:

    int threeSumMulti(vector<int>& A, int target) {
        int mod=1e9+7;
        int ans=0;
        sort(A.begin(),A.end());
         int n=A.size();
       for(int i=0;i<n-2;i++){
     
           int l=i+1,r=n-1;
          
           while(A[l]<A[r]){
                int sum= A[l] + A[r]+A[i] ;
               if(sum> target) r--;
               else if(sum < target)l++;
               else{
                    int tl = l;
                    int tr = r;
                    while (A[l] == A[tl])l++;
                    while (A[r] == A[tr])r--;
                    ans += (l - tl) * (tr - r);
                    ans %= mod;

               }
           }
           if(A[l]==A[r] && A[l]+A[r]==target-A[i]){
                int d = r - l + 1;
                ans+=(d)*(d-1)/2;
                ans%=mod;
           }
       }
       return ans;
    }
};

 

同理,如果五数之和就可以固定三个数,再用双指针。

细节决定成败,思路都对,测试也过了,就是提交不成功,这种感觉真的很难受,小菜鸡还是一步步慢慢来吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值