今天,带来哈希表相关算法的讲解。文中不足错漏之处望请斧正!
1. 四数相加2
分析题意
求符合条件的四元组的出现次数,条件:
- nums1
- nums2
- nums3
- nums4
从四个数组中的每一个数组取一个数 num1, num2, num3, num4,满足num1 + num2 + num3 + num4 == 0
,则这是一个满足条件的四元组,可以记上它的出现次数。
题意转化
可以简单转化为 直接遍历取得4个数, 判断是否满足条件.但太慢,时间复杂度O(n^4)。
其实可以动动脑筋,将题意转化为 是否存在 两个两数之和 sum1 和 sum2 相加为0。
解决思路
四个数组该怎样去遍历,建立映射?
我们可以先遍历前两个数组,将数组中的元素两两求和得到sum1,把sum1和其出现次数建立映射得到哈希表sums1。接着遍历后两个数组,也两两求和得到sum2,在sums1中O(1)查找是否有一个和,和当前sum相加为0。
但为什么要这样遍历,我先遍历一个建立映射,再遍历三个不行吗?
这样我们在最终搜索比对的时候需要3层for来玩儿,那就是O(n^3)。而我们两两遍历只需要O(2 * n^2),这才是更好的。
编程实现
class Solution {
public:
// 四数之和的判断 拆分为 两数之和的判断
// 先遍历两个数组并求得所有两数之和sums1, 再遍历两个数组求剩下的两数之和, 查找是否有sum1 = -sum2
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> sums1; // <sum1, cnt> -- 题目不要求返回下标, 只用返回次数
int sum1 = 0;
int sum2 = 0;
// 先遍历两个数组并求得所有两数之和sums1
for (int &num1 : nums1) {
for (int &num2 : nums2) {
sum1 = num1 + num2;
++sums1[sum1];
}
}
// 再遍历两个数组求剩下的两数之和, 查找是否有sum1 = -sum2
int cnt = 0;
for (int &num3 : nums3) {
for (int &num4 : nums4) {
sum2 = num3 + num4;
auto iter = sums1.find(-sum2);
if (iter != sums1.end()) cnt += iter->second;
}
}
return cnt;
}
};
时间复杂度:O(n^2)
2. 赎金信
分析题意
“给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。”
题意转化
判断ransomNote的组成字符是否全部都在magazine中有足够的字符与之对应。
解决思路
查找,上哈希。遍历magazine,用哈希表描述magazine中的字符出现过多少次。
编程实现
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int appeared[26] = {0};
// 用哈希表(数组)描述magazine中的哪些字符出现过.
for (char &ch : magazine) ++appeared[ch - 'a'];
// 在magazine中查找ransomNote的所有字符, 所有都能找到才是赎金信.
for (char &ch : ransomNote) {
--appeared[ch - 'a'];
if (appeared[ch - 'a'] < 0) return false; // magazine中没有足够字符构成ransomNote
}
return true;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
今天的分享就到这里了,感谢您能看到这里。
这里是培根的blog,期待与你共同进步!