算法训练营 Day6 哈希表 2 | 454.四数相加 II | 383.赎金信 | 15.三数之和 | 18.四数之和

74 阅读4分钟

算法训练营 Day6 哈希表 2 | 454.四数相加 II | 383.赎金信 | 15.三数之和 | 18.四数之和

查阅文档地址:programmercarl.com/

本期题目地址:

  1. 383.赎金信 - 简单 - 力扣
  2. 454.四数相加 II - 中等 - 力扣链接
  3. 15.三数之和 - 中等 - 力扣
  4. 18.四数之和 - 中等 - 力扣

目录:

  1. 基本概念(做题前要理解的概念)
  2. 我的解法
  3. 疑问点(过程中产生了问题并且查找资料解决)

语言

采用C++,一些分析也是用于 C++,请注意。

454.四数相加 II

454.四数相加 II - 中等 - 力扣链接

我的代码


class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        std::unordered_map<int, int> hashmap;
        // 初始化
        for (int i = 0; i < nums1.size(); ++i) {
            for (int j = 0; j < nums2.size(); ++j) {
                hashmap[nums1[i] + nums2[j]]++;
            }
        }

        int res = 0;
        for (int i = 0; i < nums3.size(); ++i) {
            for (int j = 0; j < nums4.size(); ++j) {
                int target = 0 - (nums3[i] + nums4[j]);
                if (hashmap.count(target)) {
                    res += hashmap[target];
                }
            }
        }

        return res;
    }
};

我的疑惑

  1. 本题是独立的四个数组,是哈希表的经典题目。进阶:给出一个数组(而不是四个数组),在这里找出四个元素相加等于 0,答案中不可以包含重复的四元组。
  2. 我刚开始使用哈希表法但是没办法通过所有的例子,使用暴力需要三层循环。
  3. 使用哈希表的技巧:遍历 nums3 和 nums4 中元素两两相加的结果,查找其相反数在 hashmap 中出现的次数,并累加到 res 中。

383.赎金信

383.赎金信 - 简单 - 力扣

我的代码

// 题目限定在 26 个小写字母,本题可以开长度 26 的动态数组模拟哈希数组
// 时间复杂度:O(n+m)
// 空间复杂度:O(n);n = 26 长度;
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        if(ransomNote.length() > magazine.length()) {
            return false;
        }

    vector<int> vec(26, 0);
    for(auto ss : magazine) {
        vec[ss-'a'] ++;
    }
    for(auto ss : ransomNote) {
        vec[ss-'a'] --;
        if(vec[ss-'a'] < 0) {
            return false;
        }
    }
    return true;
    }
};

15.三数之和

15.三数之和 - 中等 - 力扣

我的代码

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;

        sort(nums.begin(), nums.end());

        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] > 0)
                break;

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

            int left = i + 1, right = nums.size() - 1;

            while (left < right) {
                int temp = nums[i] + nums[left] + nums[right];
                if (temp == 0) {
                    res.push_back({nums[i], nums[left], nums[right]});
                    // 问题一:找到一组后没有剪枝
                    left++;
                    right--;
                    while (left < right && nums[left] == nums[left - 1])
                        left++;
                    while (left < right && nums[right] == nums[right + 1])
                        right--;
                } else if (temp > 0) {
                    right -= 1;
                } else if (temp < 0) {
                    left += 1;
                }
            }
        }
        return res;
    }
};

18.四数之和

18.四数之和 - 中等 - 力扣

我的代码

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;

        sort(nums.begin(), nums.end());

        for (int i = 0; i < nums.size(); i++) {
            // 问题一:如何剪枝。target 可能是负数,不能因为 -4>-10 而跳过。剪枝额度前提是都是正数。
            if ((nums[i] > 0 && target > 0) && nums[i] > target) {
                break;
            }

            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            for (int j = i + 1; j < nums.size(); j++) {
                if ((nums[j] > 0 && target > 0) && nums[i] + nums[j] > target) {
                    break;
                }
                // j > i + 1
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                int left = j + 1, right = nums.size() - 1;
                while (left < right) {
                    // 问题二:四数相加会溢出,需要在式子里添加强制转换(long long)
                    long long temp = (long long)nums[i] + nums[j] + nums[left] + nums[right];
                    if (temp == target) {
                        res.push_back({nums[i], nums[j], nums[left], nums[right]});

                        left++;
                        right--;
                        while (left < right && nums[left] == nums[left - 1])left++;
                        while (left < right && nums[right] == nums[right + 1])right--;
                    } else if (temp > target) {
                        right -= 1;
                    } else if (temp < target) {
                        left += 1;
                    }
                }
            }
            
        }
        return res;
    }
};

总结

哈希表的核心作用:快速判断元素是否存在在容器中。 基础知识:知道哈希函数,哈希碰撞。只有对 set、map、vector 数据结构的底层实现很熟悉,才能灵活使用,否则很容易写出效率低下的程序。

  1. 数组作为哈希表:
  • 大小限制条件明显
  • 242.有效的字母异位词
  • 383.赎金信
  1. set 作为哈希表:
  • 349.两个数组的交集
  • 202.快乐数
  • 集合:不重复出现
  1. map 作为哈希表:
  • 1.两数之和
  • 454.四数相加
  1. 快慢指针:
  • 18.四数之和
  • 15.三数之和

本文围绕算法训练营 Day5 的四道力扣题目展开,采用 C++ 语言给出解题代码,并分析过程中的疑惑。在 454.四数相加 II 中,借助哈希表统计两组数组元素和,通过查找相反数统计结果,并思考了数组整合后的进阶问题。383.赎金信利用数组模拟哈希表解决。15.三数之和与 18.四数之和则使用排序和双指针法,过程中关注剪枝与数据溢出问题。文末总结了哈希表在算法题中的应用,以及数组、set、map 作为哈希表的典型场景,还提及快慢指针在三数、四数之和问题中的运用。