代码随想录讲解
1.哈希表用来解决什么问题
一般哈希表都是用来快速判断一个元素是否出现集合里。
2.常见的三种哈希结构
集合 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::set | 红黑树 | 有序 | 否 | 否 | O(log n) | O(log n) |
std::multiset | 红黑树 | 有序 | 是 | 否 | O(logn) | O(logn) |
std::unordered_set | 哈希表 | 无序 | 否 | 否 | O(1) | O(1) |
std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
映射 | 底层实现 | 是否有序 | 数值是否可以重复 | 能否更改数值 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
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) |
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。
3.适用场景:
数组:存一个数据或可以存多个数据,如字母的异位单词,相当下标存了元素,而值存出没出现过,相当于set,又如赎金信,下标存数据,里面的值存数组个数,相当于map
效率高,但适用于范围小,所以数组包含了他们两个,如果元素很分散就也不能用数组,如果元素不分散,像后面存字母这种,就用数组
set:存一个数据,unordered_set效率高,一般用它,当要有序时用set,有重复元素时用multiset
map:存两个数据,和上面一样,一般用pair存数据
c++代码(数组):
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26] = {0};
for (int i = 0; i < s.size(); i++) {
record[s[i] - 'a']++;
}
for (int i = 0; i < t.size(); i++) {
record[t[i] - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (record[i] != 0) {
return false;
}
}
return true;
}
};
思路
判断两个字符串里的元素组成是否相同,
暴力解法:两层for循环,判断每个数是否在另一个集合中出现过
所以我们可以用哈希表来做
1.因为数据很小,且允许重复,用数组
2,因为需要把每个数都映射到数组中,有26个字母,所以可以开一个26空间的大小的数组来储存数据,有重复的数据,在那个位置上++
3.最后遍历第二个数组,出现了相同数字,就--
4.如果最后数组还是为0,就是正确的
代码
字符怎么变数字
t[i] - 'a'
c++代码(set)
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
unordered_set result_set;
unordered_set nums_set(nums1.begin(), nums1.end());
for (int i = 0; i < nums2.size(); i++) {
if ( nums_set.find(nums2[i]) != nums_set.end()) {
result_set.insert(nums2[i]);
}
}
return vector(result_set.begin(), result_set.end());
}
};
思路
求两个数组的交集
暴力做法:两层for循环判断每个元素是否在另一个数组中,是则加入到一个新数组中,返回新数组
1.判断每个元素是否在另一个数组中可以看出用hash表
2.这题改了,因为数据过大一般用set类型,因为是无序的和去重,用unordered_set
3.把第一个数组中的元素加入一个set中,遍历第二个元素,如果出现的话,就加入新数组
4.返回新数组
代码
unordered_set nums_set(nums1.begin(), nums1.end());
for (int i = 0; i < nums2.size(); i++) {
if ( nums_set.find(nums2[i]) != nums_set.end()) {
result_set.insert(nums2[i]);
}
}
return vector(result_set.begin(), result_set.end());
c++代码(set)
class Solution {
public:
int getSum (int x) {
int sum = 0;
while (x > 0) {
int t = x / 10;
sum += (x%10) * (x%10);
x = t;
}
return sum;
}
bool isHappy(int n) {
unordered_setresult;
result.insert(n);
while (1) {
int t = getSum(n);
if (t == 1) {
return true;
}
if (result.find(t) != result.end()) {
return false;
} else {
result.insert(n);
}
n = t;
}
}
};
思路
示例:
输入:19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
由此可以看出求出最后是否和是否等于1,每次出现的求和数前面一定没有出现过,不然一定会陷入死循环
1.判断一个数是否出现过由此可以看出需要使用哈希法
2.因为范围1 <= n <= 231 - 1且不需要重复元素,还是无序的,用unordered_set
3.可以从第一个数不断求和,如果没出现过这个数就加入到set中,出现过就死循环返回false
4.当求和结果为1,就返回true
代码
if (result.find(t) != result.end()) {
return false;
} else {
result.insert(n);
}
c++代码(map)
class Solution {
public:
vector twoSum(vector& nums, int target) {
unordered_map result;
for (int i = 0; i < nums.size(); i++) {
auto val = result.find(target - nums[i]);
if ( val != result.end()) {
return {val->second, i};
}
result.insert(pair(nums[i], i));
}
return {};
}
};
思路
寻找数组中两个元素,如果相加为目标值,就返回这两个元素的下表
遍历这个数组,如果这个数组中这个元素的互补值在前面出现过,添加到map中了,就找到了
1.查看元素有没有出现过用哈希表,因为储存两个元素(值和下标),用map,无序且效率用unordered_map
2.遍历数组,查元素的互补值,如果这个互补值在前面出现过就找到
3.如果没出现过,就把这个元素加入到map中,继续遍历数组,找不到返回空map
代码
auto val = result.find(target - nums[i]);
if ( val != result.end()) {
return {val->second, i};
}
result.insert(pair(nums[i], i));