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

454.四数相加II

本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说我们都是舍空间 换时间, 工业开发也是这样。

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -2^28 2^28 - 1 之间,最终结果不会超过 2^31 - 1

思路

1.我们可以将四个数组分成两部分,A B 为一组,C D 为另外一组。

2.对于 A B,我们使用二重循环对它们进行遍历,得到所有 A[i]+B[j] 的值并存入哈希映射中。对于哈希映射中的每个键值对,每个键表示一种 A[i]+B[j],对应的值为 A[i]+B[j] 出现的次数。

3.对于 C D,我们同样使用二重循环对它们进行遍历。当遍历到 C[k]+D[l] 时,如果 −(C[k]+D[l]) 出现在哈希映射中,那么将 −(C[k]+D[l]) 对应的值累加进答案中。

最终即可得到满足 A[i]+B[j]+C[k]+D[l]=0 的四元组数目。

C++代码如下:

class Solution {

public:

    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {

        unordered_map<int,int> hashmap;//key:s1+s2的数值,value:s1+s2数值出现的次数

        //遍历s1s2数组,统计两个数组元素之和还有出现的次数,放到map

        for(int a:nums1){

            for(int b:nums2){

                hashmap[a+b]++;

            }

        }

        int count=0;//统计a+b+c+d=0 出现的次数

        //遍历s3s4数组,如果找到-(c+d)map中出现过的话,就把mapkey对应的value累加到count

        for(int c:nums3){

            for(int d:nums4){

                if(hashmap.find(-(c+d))!=hashmap.end()){

                    count+=hashmap[-(c+d)];

                }

            }

        }

        return count;

    }

};

注意点:搞明白哈希表的keyvalue分别代表什么?

383.赎金信

本题  242.有效的字母异位词 是一个思路 ,算是拓展题

题目: 给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。


思路

这道题目和242有效的字母异位词很像,242相当于求 字符串a 字符串b 是否可以相互组成 ,而这道题目是求 字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a

C代码如下:

bool canConstruct(char* ransomNote, char* magazine) {

    //创建哈希表统计两个字符串中每个字母出现的次数

        int recordNote[26]={0};

        int recordZine[26]={0};

        for(int i=0;i<strlen(ransomNote);i++){

            recordNote[ransomNote[i]-'a']++;

        }

        for(int i=0;i<strlen(magazine);i++){

            recordZine[magazine[i]-'a']++;

        }

        for(int i=0;i<26;i++){

            if(recordNote[i]>recordZine[i]){//如果成立,说明ransomNote中某个字母个数比magazine

                return false;

            }

        }

        return true;

    }

   

15.三数之和

本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解

题目:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k  j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

思路

1.首先对数组进行排序,这样方便后续的操作。

2.遍历排序后的数组,使用双指针法来寻找满足要求的三元组。

3.固定一个数(假设为nums[i]),然后使用双指针leftright分别指向i+1和末尾元素。

4.如果nums[i]大于零,由于数组是有序的,后续的元素都会大于零,所以不存在满足条件的三元组,可以直接返回结果。

5.如果i大于0nums[i]等于nums[i-1],说明已经考虑过这个数,为了避免重复,我们跳过这个数。

6.根据nums[i]nums[left]nums[right]的和来判断如何调整指针:

如果和大于零,那么将right指针左移一位。

如果和小于零,那么将left指针右移一位。

如果和等于零,将这个三元组加入结果数组中,并同时将leftright指针向内移动,跳过重复元素。

7.重复步骤36,直到遍历完数组所有元素。

8.返回结果数组。

C++代码如下:

class Solution {

public:

    vector<vector<int>> threeSum(vector<int>& nums) {

        int n=nums.size();

        sort(nums.begin(),nums.end());//对数组进行排序

        vector<vector<int>> answer;//存储结果的二维向量

        for(int i=0;i<n-2;i++){//遍历数组,固定第一个元素

        //三元组元素a去重

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

               continue;

            }

           int left=i+1;//左指针指向固定元素的下一位

           int right=n-1;//右指针指向数组末尾

           while(left<right){

               int sum=nums[i]+nums[left]+nums[right];//计算三个元素的和

               if(sum<0){//如果和小于0,说明需要增大和,左指针右移一位

               left++;

                }

               else if(sum>0){//如果和大于0,说明需要减小和,右指针左移一位

               right--;

               }

               else{//和等于零,找到满足条件的三元组

               answer.push_back({nums[i],nums[left],nums[right]});//将三元组添加到结果中

               //避免重复的左指针元素

               while(left<right && nums[left]==nums[left+1]){

                   left++;

               }

               //避免重复的右指针元素

               while(left<right && nums[right]==nums[right-1]){

                   right--;

               }

               //找到答案时,双指针同时收缩

               left++;//左指针右移一位

               right--;//右指针左移一位

               }

           }

        }

        return answer;

    }

};

难点:三元组元素(a,b,c)如何去重以及在程序的哪个位置去进行去重。

18.四数之和

要比较一下,本题和 454.四数相加II 的区别,为什么454.四数相加II会简单很多,这个想明白了,对本题理解就深刻了. 本题思路整体和三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意.

题目: 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 ab,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

思路

四数之和,15三数之和是一个思路,都是使用双指针法, 基本解法就是在三数之和的基础上再套一层for循环

三数之和的双指针解法是一层for循环num[i]为确定值,然后循环内有leftright下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有leftright下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况.

C++代码如下

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 && target>=0)break;//k剪枝(如果target是负数,负数相加会变小)

            if(k>0 && nums[k]==nums[k-1])continue;//k去重

            for(int i=k+1;i<nums.size();i++){//i遍历第二个数

                if(nums[k]+nums[i]>target && target>=0)break;//二级i剪枝

                if(i>k+1 && nums[i]==nums[i-1])continue;//二级i去重

                int left=i+1;

                int right=nums.size()-1;

                while(left<right){

                    //sum =nums[k]+nums[i]+nums[left]+nums[right];会溢出

                    long sum =(long)nums[k]+nums[i]+nums[left]+nums[right];

                    if(sum<target)left++;

                    else if(sum>target)right--;

                    else{

                        result.push_back({nums[k],nums[i],nums[left],nums[right]});//存放到结果

                        while(left<right && nums[left]==nums[left+1])left++;//left去重

                        while(left<right && nums[right]==nums[right-1])right--;//right去重

                        left++;

                        right--;                   

                        }

                }

            }

        }

        return result;

    }

};

难点:搞明白四元组(a,b,c,d)分别如何剪枝加去重,以及在程序里什么位置进行。

写在最后:今天哈希表的题目难度比昨天高了一大截,也只是大概了解了每个题的解题思路,具体细节还需要再慢慢消化吸收。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值