代码随想录算法训练营第七天 454.四数相加 383.赎金信 15.三数之和 18.四数之和

454.四数相加

        这道题要求找的是从四个数组中各挑一个元素找加起来等于0的组合的个数,按照常规的暴力解,肯定就是四个for循环遍历,找到了,count++;但是这样子的时间复杂度太高了,所以需要想一下降低时间复杂度的方法,因此有了两两组合的方法,也就是先遍历第一个和第二个的数组,把他们的和以及出现的次数记录在map里面,然后遍历第三个和第四个数组,看看0-(c+d)是否存在map里面,存在的话,count+=记录在map里面的值,两两组合的话时间复杂度就是O的平方,因为是两个for循环。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, 
                     vector<int>& nums3, vector<int>& nums4)
    {
      unordered_map<int,int>umap;
      for(int a:nums1)
      {
          for(int b:nums2)
          {
              umap[a+b]++;
          }
      }
      int count=0;
      for(int c:nums3)
      {
          for(int d:nums4)
          {
              if(umap.find(0-(c+d))!=umap.end())
              {
                  count+=umap[0-(c+d)];
              }
          }
      }
      return count;
    }
};

383.赎金信

1.暴力解法

        因为要查找magazine的字符串的字母能否拼成ransomNote,(注意,magazine的字母只能用一次),所以我们可以遍历magazine的字母,然后遍历ransomNote,即双层遍历,这样的话,一旦在magazine的字母有在ransomNote,那么所对应ransomNote的字母就移除。最后看ransomNote的字符串长度是否为0,如果为0的话说明magazine的字符串的字母可以拼成ransomNote。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, 
                     vector<int>& nums3, vector<int>& nums4)
    {
      unordered_map<int,int>umap;
      for(int a:nums1)
      {
          for(int b:nums2)
          {
              umap[a+b]++;
          }
      }
      int count=0;
      for(int c:nums3)
      {
          for(int d:nums4)
          {
              if(umap.find(0-(c+d))!=umap.end())
              {
                  count+=umap[0-(c+d)];
              }
          }
      }
      return count;
    }
};

2.哈希法

        哈希法的解法跟242判断有效的字母异位词相似,先用数组存储ransomNote每一个字母的出现次数,用asciii码-‘a’作为数组的下标,然后再遍历magazine,magazine的字母-'a'所对应的数组元素--,如果record[i]>0,说明ransomNote里面有的字母在magazine是没有的或者是个数不够凑成ransomNote,因为如果个数刚好可以凑或者有多余的字母,对应的数组的值是会<=0的。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        // for(int i=0;i<magazine.length();i++)
        // {
        //     for(int j=0;j<ransomNote.length();j++)
        //     {
        //         if(magazine[i]==ransomNote[j])
        //         ransomNote.erase(ransomNote.begin()+j);
        //         break;
        //     }
        // }
        // if(ransomNote.length()==0)
        // return true;
        // return false;
         int record[26]={0};
    for(int i=0;i<ransomNote.length();i++)
    {
        record[ransomNote[i]-'a']++;
    }
    for(int i=0;i<magazine.length();i++)
    {
        record[magazine[i]-'a']--;
    }
    for(int i=0;i<26;i++)
    {
        if(record[i]>0)
        return false;
    }
    return true;
    }
};

15.三数之和

        这是一道坑点贼多的题,第一步要先将数组排序!!!!!最主要的就是要去重,第一层去重,是在找与nums[i]匹配的集合,去重的点在于while(nums[left]==nums[left+1]&&left<right) left++;   while(nums[right]==nums[right-1]&&left<right) right--;这一步是为了确保在搜集的过程中不会出现重复的情况,如果元素相同,那么指针都会往右或往左移,第二层去重,是去重和nums[i]==nums[i-1]相同的情况,因为如果nums[i]==nums[i-1],那说明nums[i]这个元素的集合已经在之前搜集nums[i-1]的时候找过了,所以这时候指向i的指针可以往后移动,注意在这里判断条件要是i>0&&nums[i]==nums[i-1],因为如果i=0的话,那访问是非法的,会报错。

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

18.四数之和

        四数之和简直就是三数之和的超强plus版,与三数之和不同的是在移动left+right指针前要进行两次循环,其中每一次循环都有去重和剪枝的操作,

 if(nums[i]>target&&nums[i]>=0)

              break;

这里要加上nums[i]>0的原因是因为这个数组中可能有负数,target也可能是负数,假如target=-5,nums[0]=-4,nums[1]=-1,nums[2]=0,nums[3]=0,那么这时候是找得出加起来等于target的集合的,所以咱们得限制num[i]>0才可以剪枝,不然像这种情况是不可以剪枝的。

 if(i>0&&nums[i]==nums[i-1])

              continue;

这是去重的操作,因为如果nums[i]==nums[i-1]那么说明这个元素在查找num[i-1]的时候已经被用到了,所以咱们可以再继续往后移找没有用过的新元素。

这道题的其它代码和三数之和很像,基本就是 套娃再套娃。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>>result;
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]>target&&nums[i]>=0)
              break;
            if(i>0&&nums[i]==nums[i-1])
              continue;
            for(int j=i+1;j<nums.size();j++)
            {
                if(nums[i]+nums[j]>target&&nums[i]+nums[j]>=0)
                  break;
                if(j>i+1&&nums[j]==nums[j-1])
                  continue;
                  int left=j+1;int right=nums.size()-1;
                while(left<right)
                {if((long)nums[i]+nums[j]+nums[left]+nums[right]>target)
                right--;
                else if ((long)nums[i]+nums[j]+nums[left]+nums[right]<target)
                left++;
                else
                {
                    result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
                    while(nums[right]==nums[right-1]&&left<right)
                    right--;
                    while(nums[left]==nums[left+1]&&left<right)
                    left++;
                    right--;
                    left++;
                }
                }
            }
        }
        return result;
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值