算法训练Day06——哈希
今日目标
● 哈希表理论基础
● 242.有效的字母异位词
● 349. 两个数组的交集
● 202. 快乐数
● 1. 两数之和
哈希表理论基础
引用 代码随想录
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法
哈希表
哈希表是根据关键码的值而直接进行访问的数据结构。
哈希函数
指将哈希表中元素的关键键值映射为元素存储位置的函数。可以将任意长度的“字节串”映射为一个固定长度的“大整数”。哈希函数的特点如下:
哈希函数在计算机上的应用包括保证数据的完整性、数据加密、数字签名等。常见的哈希函数有md系列算法、sha系列算法、sm3等。(百度百科)
哈希碰撞
Hash碰撞是指两个不同的输入值,经过哈希函数的处理后,得到相同的输出值,这种情况被称之为哈希碰撞。
一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
- 拉链法
链式哈希表是一种将哈希值相同的元素存储在同一个链表中的哈希表。当发生哈希碰撞时,我们只需要将新元素添加到对应的链表中即可。这样可以保证哈希表的性能和正确性。 - 线性探测法
感觉就是多分配点空间,冲突就下一个
集合 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::set | 红黑树 | 有序 | 否 | 否 | O(log n) | O(log n) |
std::multiset | 红黑树 | 有序 | 是 | 否 | O(logn) | O(logn) |
std::unordered_set | 哈希表 | 无序 | 否 | 否 | O(1) | O(1) |
std::map | 红黑树 | key有序 | key不可重复 | key不可修改 | O(logn) | O(logn) |
std::multimap | 红黑树 | key有序 | key可重复 | key不可修改 | O(log n) | O(log n) |
std::unordered_map | 哈希表 | key无序 | key不可重复 | key不可修改 | O(1) | O(1) |
红黑树——平衡二叉树,有序但更新数值需要重建二叉树,因此不可更改
思考:什么时候用数组,什么时候用set或map
242.有效的字母异位词
思路:unordered_map或者数组,s出现一次+一次,t出现一次-一次,最终全为0相等。
bool isAnagram(string s, string t) {
unordered_map<char, int> hash;
int lens = s.size();
int lent = t.size();
if(lens != lent) return false;
for(int i = 0; i < lens; i++) {
hash[s[i]]++;
hash[t[i]]--;
}
for(int i = 0; i < hash.size(); i++) {
if(hash[i] != 0) {
return false;
}
}
return true;
}
349.两个数组的交集
思路: 两数组都有重复数据,可以先去重,遍历后将交集加入vector
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> hash(nums1.begin(), nums1.end());
unordered_set<int> hash2(nums2.begin(), nums2.end());
vector<int> res;
for(int i : hash2) {
if(hash.find(i) != hash.end()){
res.push_back(i);
}
}
return res;
}
202.快乐数
思路: 没啥思路,做过一遍还不会
int getSum(int n) {
int sum = 0;
while(n) {
sum += (n%10) * (n%10);
n /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> hash;
int sum = getSum(n);
while(hash.find(sum) == hash.end()) {
if(sum == 1) return true;
hash.insert(sum);
sum = getSum(sum);
}
return false;
}
引用自代码随想录:
1.两数之和
思路:遍历一次数组,拿到target-nums[i]的集合,再从nums中找是否有交集。
2. 遍历一次数组每次都判断nums[i]是否在集合里,不在保存target-nums[i],在的话说明当前数存在对应数字,结束。用unordered_map保存下标。
耗时30分钟。
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash;
for(int i = 0; i < nums.size(); i++) {
if(hash.find(nums[i]) != hash.end()){
return vector<int>{i, hash[nums[i]]};
} else{
hash[target - nums[i]] = i;
}
}
return {};
}