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

1-文章和视频链接

1.1 454.四数相加II-代码随想录
1.2 454.四数相加II-卡哥B站视频讲解
1.3 383.赎金信-代码随想录
1.4 15.三数之和-代码随想录
1.5 15.三数之和-卡哥B站视频讲解
1.6 18.四数之和-代码随想录
1.7 18.四数之和-卡哥B站视频讲解


2-题目

第一题:454.四数相加II

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> map;      // 定义一个unordered map
                                          // key是取nums1和nums2中各一个值的两值之和
                                          // value是此和出现的次数

        for(int a:nums1){                 // 遍历数组1
            for(int b:nums2){             // 对于数组1中每个数,遍历数组2
                map[a+b]++;               // map中出现的和的次数统计
            }
        }

        int count = 0;                    // 定义一个计数器,存放元组个数,最后返回的值
        for(int c:nums3){                 // 遍历数组3
            for(int d:nums4){             // 对于数组3中每个数,遍历数组4
                if(map.find(0-(c+d))!=map.end()){   // 如果满足(a+b+c+d)=0的key
                    count += map[0-(c+d)];          // 计数器加上符合条件的value值
                }
            }
        }
        return count;                     // 返回计数器
    }
};
  • 思路: 原本是暴力解法四个循环嵌套,看了卡哥的视频,2个平行的2个嵌套循环。因为我们需要最后返回元组个数,即出现次数,所以定义一个map,key值为数组1和数组2中各取1个元素相加的和的集合,value则为该和出现的次数;然后构建map,用2个循环嵌套计算数组1和数组2中两两元素相加和及他们的和出现次数;再用2个循环嵌套查找满足数组1-4中各取一数相加和为0的key,这里的key用0-数组3元素-数组4元素=数组1元素和数组2元素的和,即刚刚构建map中的key值;找到的话,计数器加上之前满足条件的key的value值。
  • 实现难点: 计数器不是累乘而是累加,计数器并不是+1,而是加上满足条件的key的value值。
  • 收获: 理解了map的用法。

第二题:383.赎金信

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[26] = {0};   // 定义记录数组,每个元素对应字母位置

        // 如果赎金信中字符串长度超过了杂志中字符串长度,杂志肯定无法构成赎金信
        if(ransomNote.length()>magazine.length()) return false;

        for(int i=0; i<magazine.length(); i++){   // 遍历杂志中每个字母
            record[magazine[i]-'a']++;            // 杂志中的字母,记录数组中对应位置+1
        }

        for(int j=0; j<ransomNote.length(); j++){  // 遍历赎金信中每个字母
            record[ransomNote[j]-'a']--;           // 赎金信中的字母,记录数组中对应位置-1

            // 如果记录数组中赎金信对应位置上的字母个数为负
            // 说明赎金信中有杂志中没有的字母,返回假
            if(record[ransomNote[j]-'a']<0) return false;
        }

        return true;  // 到达此处,返回真
    }
};
  • 思路: 暴力解法两个循环嵌套擦除后判断长度是否为0;哈希解法中定义一个数组,存放magazine中每个字母的对应位置,然后用ransomNote去判断所有字母是否全部包括。
  • 实现难点: 别忘记先判断赎金信与杂志中字符串长度(虽然无此判断,力扣也能过)。
  • 收获: 哈希数组在26个字母上很好用。

第三题:15.三数之和

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;        // 定义一个结果数组,可接收多个三元组
        sort(nums.begin(), nums.end());    // 双指针法,先对nums数组排序
        for(int i=0; i<nums.size(); i++){  // 遍历nums数组
            if(nums[i]>0) return result;   // 如果第一个数超过0,之后不可能产生三数相加为0
            if(i>0 && nums[i]==nums[i-1]) continue;  // 第一个数去重,先得到一个结果再去

            int left = i + 1;               // 定义左指针
            int right = nums.size() - 1;    // 定义右指针
            while(right>left){              // 指针移动的循环,直到左右指针相遇
                // 三数相加>0,右指针缩减后退
                if(nums[i]+nums[left]+nums[right]>0) right--;
                // 三数相加>0,左指针往前
                else if(nums[i]+nums[left]+nums[right]<0) left++;
                else{   // 三数相加=0,满足条件
                    // 将三元组存入结果数组中
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});

                    // 第二和第三个数的去重,一定要先得到一个结果再去重
                    while(right>left && nums[right]==nums[right-1]) right--;
                    while(right>left && nums[left]==nums[left+1]) left++;

                    // 存入数组后,2个指针都向对方一步
                    right--;
                    left++;
                }
            }
        }
        return result;   // 返回结果数组
    }
};

- 思路: 一开始用哈希法没想出来,后来看了双指针解法,茅塞顿开。遍历数组并定义左右指针。如果三数之和大于0,说明右指针指向的数太大,往后退去试试;如果三数之和小于0,说明左指针指向的数太小,向前移再试试;如果三数之和等于0,就是要找的三元组。
- 实现难点: 关键在于三元组内第一第二第三个元素如何去重以及去重的时机。第一个元素的第一次尝试一定要先经过一遍,不然容易漏解,所以判断去重的条件不应该是nums[i]==nums[i+1]而是i>0 && nums[i]==nums[i-1]。第二第三个元素的去重,在双指针的循环中,也是要得到一个三元组的结果后再去重。
- 收获: 一定要先得到一个结果,再去重;先去重,再存入。

第四题:18.四数之和

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;                  // 定义结果数组,存放四元组
        sort(nums.begin(), nums.end());              // nums数组排序
        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(right>left){
                    // 转换成long为了不溢出
                    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(right>left && nums[right]==nums[right-1]) right--;
                        while(right>left && nums[left]==nums[left+1]) left++;

                        // 左右指针向内缩减
                        right--; 
                        left++;
                    }
                }
            }
        }
        return result;    // 返回结果数组
    }
};

- 思路: 在三数相加题目基础上,在最外层再嵌套一个循环,固定前2个元素之和,然后再移动双指针即可。
- 实现难点: 如果仅判断固定值和target大小是会漏解,举例-4>-10,后面负数相加也能与target相等,所以还要加上固定值是正数的判断条件。
- 收获: 溢出时格式转换。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值