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

LeetCode454

2025-01-27 17:25:34 星期一

题目描述:力扣454
文档讲解:代码随想录(programmercarl)454.四数相加II
视频讲解:《代码随想录》算法视频公开课:学透哈希表,map使用有技巧!LeetCode:454.四数相加II

代码随想录视频内容简记

梳理

关于这道题目为什么需要用到哈希法?因为按照正常的解法,需要先遍历A和B两个数组,之后将所有找到的和存放到哈希表中,之后以同样的方式遍历C和D数组,并将对应的值进行查询。

关于为什么要使用map的结构?因为按照题目要求,最后求的是符合条件的四元组的个数,所以必须要有一个value值来对所有符合条件的情况进行记录。

大致代码内容

  1. 遍历A和B数组。map[a+b]++,将A和B遍历的结果存放到哈希表

  2. 遍历C和D数组。这里其实遍历的是之前A和B相加的结果是否在表中,所以需要查询map[0-(c+d)]++

  3. 最后统计所有符合条件的value值的和

LeetCode测试

注意这个代码的时间复杂度为O(n),代码比较简单,完整四数相加Ⅱ代码如下

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

LeetCode383

题目描述:力扣383
文档讲解:代码随想录(programmercarl)383. 赎金信

梳理

  1. 这道题和242. 有效的字母异位词很相近。一开始是没有想到的,看了一眼文档讲解,说可以定义一个数组来操作,就明白了

  2. 首先遍历magazine字符串,将其所有的小写字母存放到哈希表中。其实感觉数组这个哈希结构和map有相似之处,只要是连续的地址,数组中也可以存放一个“value值”(当然并不是真的value)

  3. 之后遍历ransomNote字符串,将对应的元素全部进行--操作

  4. 最后再检查是否有元素小于0即可

LeetCode测试

点击查看代码
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int num[26] ={0};
        for (int i = 0; i < magazine.size(); i++) {
            num[magazine[i] - 'a']++;
        }
        for (int i = 0; i < ransomNote.size(); i++) {
            num[ransomNote[i] - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (num[i] < 0) return false;
        }
        return true;
    }
};

LeetCode15

题目描述:力扣15
文档讲解:代码随想录(programmercarl)15. 三数之和
视频讲解:《代码随想录》算法视频公开课:梦破碎的地方!| LeetCode:15.三数之和

代码随想录视频内容简记

这个题用哈希法会比较麻烦,用双指针的思路会比较好解

梳理

  1. 首先需要对数组进行排序,按照从小到大升序排列

  2. 遍历a的值,然后是对a去重。针对于三个数,a,b,c。定义一个循环遍历数组得到'a'

  3. 更新left指针和right指针

  4. 再内部定义一个循环遍历b,c的值

  5. 然后对b,c去重

大致代码内容

  1. 剪枝。if (nums[i] > 0) continue这种情况的话直接跳过,剪掉和不剪掉并不影响最后的的结果,但是剪掉可以减少缩短运行的时间。

  2. 对a去重不能使用if (nums[i] == nums[i+1])continue,这样会导致一个问题就是比如[-1,-1,2]这样的一个数组,数组内部有重复元素,这样的类型就会被直接跳过,所以需要用if (nums[i] == nums[i-1])这样的方式

  3. left = i + 1, right = nums.size() - 1。对数组的指针进行更新。

  4. 遍历b和c, 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]});
  1. 接下俩要对b和c进行去重。因为比如涉及到这种情况[-1,0,0,0,0]b和c的值重复,那么接下来不能再重复添加,所以while (right > left && nums[right - 1] == nums[right]) right--; while (right > left && nums[left] == nums[left+1]) left++,需要注意的是这里是一个循环,是while而不是if。同时需要注意:找到收获集之后,还需要进行left++right--操作,保证下一次循环的b和c是不重复的。还有就是right > left这个一定要加上,要不然没有循环的出口了,会出现超时错误

LeetCode测试

代码的时间复杂度是O(n2)。感觉确实比较复杂,细节很多

点击查看代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 遍历a
        for (int i = 0; i < nums.size(); i++) {
            // 特殊情况直接跳过
            if (nums[i] > 0) return result;
            // 对a去重
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int left = i + 1;
            int right = nums.size() - 1;
            // 遍历b,c
            while (right > left) {
                // 对b,c去重
                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 (right > left && nums[right - 1] == nums[right]) right--;
                    while (right > left && nums[left + 1] == nums[left]) left++;
                    // 找到答案时再同时收缩
                    left++; 
                    right--;
                }
                
            }
        }
        return result;
    }
};

LeetCode18

题目描述:力扣18
文档讲解:代码随想录(programmercarl)18. 四数之和
视频讲解:《代码随想录》算法视频公开课:难在去重和剪枝!| LeetCode:18. 四数之和

代码随想录视频内容简记

四数之和的思路和三数之和一摸一样,难点就在于多了一个k需要遍历,同时增加的剪枝和去重操作比较繁琐

梳理

  1. 首先仍然是对数组进行排序

  2. 一级剪枝

  3. 一级去重

  4. 二级剪枝

  5. 二级去重

  6. 和三数之和代码相同的部分

大致代码内容

  1. 对于四个数,首先用k对第一个数进行遍历for (int k = 0; k < nums.size(); k++),剪枝,if (nums[k] > target && target > 0 && nums[k] > 0) continue;,这是一级剪枝。至于为什么这里不能出现负数,而要target > 0 && nums[k] > 0?是因为如果一但出现负数,那么两个数相加就有可能变小,这是我们不希望看到的,违背了我们给他排序的本意。举个例子:[-4,-1,0,0],而target是-5,这样就没法进行剪枝了,所以我们必须要保证为正数

  2. 一级去重则是延续三数之和的思想,if (k > 0 && nums[k] == nums[k-1]) continue;

  3. i进行遍历,for (int i = k + 1; i < nums.size(); i++)。首先也是剪枝,if (nums[i] + nums[k] > target && target > 0 && num[i] + nums[k] > 0) continue

  4. 二级去重,需要进行的操作是if (i > k + 1 && nums[i] == nums[i-1]) continue;

  5. 剩下的部分仍然按照三数之和的思路进行编写

LeetCode测试

测试的时候有一个问题需要注意,正常的代码在left和right指针向中间靠拢的部分,卡在了第284个用例上,提示int型不足够计算的问题。需要在代码中添加long的转换if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) right--;

时间复杂度是O(n3),其实就是三个嵌套循环,如果看元素被操作的次数看不出来的话,就看循环之间的关系。完整四数之和代码如下:

点击查看代码
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 && target > 0) continue;
            if (k > 0 && nums[k] == nums[k-1]) continue;
            for (int i = k + 1; i < nums.size(); i++) {
                if (nums[i] + nums[k] > target && nums[i] + nums[k] > 0 && target > 0) continue;
                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[left] == nums[left + 1]) left++;
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        left++;
                        right--;
                    }
                    

                }
            }
        }
        return result;
    }
};
posted on   bnbncch  阅读(1902)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示