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

454.四数相加II

题目链接/文章讲解/视频讲解:代码随想录

解题思路

首先我们会想到暴力解法,但由于我们要进行四次循环,时间复杂度为O(n^4),会超时,因此我们要使用一种便捷的方法,我们可以将等式转换为nums1+nums2 = 0-(nums3+nums4),这样我们只需要将四个数组分为两部分,分别用两个循环即可,只需要去哈希map中寻找是否有0-(nums3+nums4) 这个元素就可以了,并且我们要将出现的次数作为value,这样我们找到后,直接让继续器+=value即可

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
         int count=0;
       unordered_map<int,int> map;
       for(int i=0;i<nums1.size();i++)
       {
           for(int j=0;j<nums2.size();j++)
           {
               auto item = map.find(nums1[i]+nums2[j]);   
               if(item!=map.end())
               {item->second++;}      //去寻找两个数组的和,如果找到则value++
               else
                  map.insert(pair<int,int>(nums1[i]+nums2[j],1));  //没找到就insert进去
           }
       }
       for(int i=0;i<nums3.size();i++)
       {
         for(int j=0;j<nums4.size();j++)
         {
              auto item = map.find(0-(nums3[i]+nums4[j]));   //再次遍历,如果找到就让count+=value,最后返回count即可
             if(item!=map.end())
                count+= item->second;
}
}
                return count;
    }
};

时间复杂度:O(n) 

383. 赎金信

题目链接/文章讲解:代码随想录

 解题思路

这题我没想用暴力解法,我直接想到的是定义一个哈希数组,因为数据较小,且只有小写字母可以用26个下标就可以存储,这题和字母的同位词那题相似,这里只要让判断的条件变成数组里有小于0的数,那么就可以不能满足条件,如果没有,则是满足条件

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
            int hash[26] ={0};
            for(int i=0;i<magazine.size();i++)
               hash[magazine[i] -'a']++;     //记录magazine中字母出现的次数
            for(int i=0;i<ransomNote.size();i++)
               hash[ransomNote[i] - 'a' ]--;   //让赎金信中的字母对应减减
            for(int i=0;i<26;i++)
               if(hash[i]<0) return false;   //如果出现了小于0的数,那么代表不能满足条件
            return true; 
    }
};

时间复杂度:O(n)

 15、三数之和

题目链接/文章讲解/视频讲解:代码随想录

解题思路

这道题要求不能有重复的三元组,因此使用哈希法再进行去重操作是非常麻烦和耗时的,因此我们可以换一种思路,用双指针法,但我们也要进行去重操作,但去重操作要如何进行呢,到底是nums[i]==nums[i+1]时跳过,还是nums[i]==nums[i-1]时跳过,我们可想而知,如果是前者就跳过,那么是三元组中不能有重复元素,而后者跳过的意思就是,当我们i-1这个下标的数已经经历过一次查过结果后,那么他已经遍历了所有a=nums[i-1]时的情况,因此不需要再走一遍流程,直接跳过即可,而b,c的去重操作也是一样的。然后再一个重要的点就是如何找到等于0的元组呢,因此不要求下标,我们可以使用sort来排序后,如果三个数相加>0,则让right左移缩小,如果<0,则让left左移增大,当到0时,我们收获一个结果后,再进行去重操作,否则会漏掉000这种情况

利用代码随想录的图来做一下演示 

 

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;   //如果第一个元素已经大于0了,无论如何都找不到符合条件的三元组,那么就直接返回结果
             if(i>0&&nums[i]==nums[i-1])  continue;   //去重,a作为有过的数已经判断过所有情况了
             int left=i+1;
             int right=nums.size()-1;
             while(right>left)     //开始移动双指针来寻找答案,等于时没有意义
             {
                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]});    //先收获一个结果,再去重,否则会遗漏000这样的情况
                        while(right>left&&nums[right]==nums[right-1])   right--;     //如果和他下一个要遍历的相同,那么就跳过这个数,并确保右指针在左指针右边
                        while(right>left&&nums[left]==nums[left+1])     left++;

                        //此时的right和left依旧指向我们找到的结果,因此要回缩指针
                        right--;
                        left++;
                }
             }
         }
         return result;
    }
};

 18、四数之和

题目链接/文章讲解/视频讲解:代码随想录

 解题思路

这题和三数之和的思路差不多,但是剪枝操作更为复杂一些,因为target可能会出现为负数的情况,因此我们剪枝只能要求target>0,并且nums[k]>0时才可以进行剪枝操作,逻辑变成nums[i] > target && (nums[i] >=0 || target >= 0)就可以了,其他的思路是一样的,注意i的取值和更新即可

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
            vector<vector<int>> result;
            sort(nums.begin(),nums.end());
            for(int k=0;k<nums.size();k++)
            { 
                 if(nums[k]>target&&nums[k]>0)  break;  //剪枝操作
                 if(k>0&&nums[k]==nums[k-1])   continue;    //去重
                 for(int i=k+1;i<nums.size();i++)
                 {
                      if(nums[k]+nums[i]>target && nums[k]+nums[i]>0)    break;//剪枝操作
                      if(i>k+1 && nums[i]== nums[i-1])     continue;      //去重
                      int left = i+1;
                      int right = nums.size()-1;
                      while(right>left)
                      {
                           if((long) nums[k]+nums[i]+nums[left] + nums[right] >target)   right--;  //四数相加可能会溢出
                           else if((long) nums[k]+nums[i]+nums[left] + nums[right] <target)  left++;
                           else
                           {
                               result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]});
                               while(right>left && nums[right] ==nums[right-1])  right--;
                               while(right>left && nums[left] ==nums[left+1])   left++;

                               right--;
                               left++;
                           }
                      }
                 }
            }
            return result;
    }
};
  • 时间复杂度: O(n^3)
  • 空间复杂度: O(1)

收获

今天的题难得批爆,人做晕了呜呜继续努力 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值