算法训练营 Day6 哈希表 2 | 454.四数相加 II | 383.赎金信 | 15.三数之和 | 18.四数之和
查阅文档地址:programmercarl.com/
本期题目地址:
目录:
- 基本概念(做题前要理解的概念)
- 我的解法
- 疑问点(过程中产生了问题并且查找资料解决)
语言
采用C++,一些分析也是用于 C++,请注意。
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;
}
};
我的疑惑
- 本题是独立的四个数组,是哈希表的经典题目。进阶:给出一个数组(而不是四个数组),在这里找出四个元素相加等于 0,答案中不可以包含重复的四元组。
- 我刚开始使用哈希表法但是没办法通过所有的例子,使用暴力需要三层循环。
- 使用哈希表的技巧:遍历 nums3 和 nums4 中元素两两相加的结果,查找其相反数在 hashmap 中出现的次数,并累加到 res 中。
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.三数之和
我的代码
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.四数之和
我的代码
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 数据结构的底层实现很熟悉,才能灵活使用,否则很容易写出效率低下的程序。
- 数组作为哈希表:
- 大小限制条件明显
- 242.有效的字母异位词
- 383.赎金信
- set 作为哈希表:
- 349.两个数组的交集
- 202.快乐数
- 集合:不重复出现
- map 作为哈希表:
- 1.两数之和
- 454.四数相加
- 快慢指针:
- 18.四数之和
- 15.三数之和
本文围绕算法训练营 Day5 的四道力扣题目展开,采用 C++ 语言给出解题代码,并分析过程中的疑惑。在 454.四数相加 II 中,借助哈希表统计两组数组元素和,通过查找相反数统计结果,并思考了数组整合后的进阶问题。383.赎金信利用数组模拟哈希表解决。15.三数之和与 18.四数之和则使用排序和双指针法,过程中关注剪枝与数据溢出问题。文末总结了哈希表在算法题中的应用,以及数组、set、map 作为哈希表的典型场景,还提及快慢指针在三数、四数之和问题中的运用。