-
哈希表理论基础
哈希表
哈希表是根据关键码的值而直接进行访问的数据结构。
哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。
一般哈希表都是用来快速判断一个元素是否出现集合里。
哈希函数
哈希函数是一种映射关系,根据数据的关键词 key ,通过一定的函数关系,计算出该元素存储位置的函数。
哈希碰撞
选用哈希函数计算哈希值时,可能不同的 key 会得到相同的结果。例如,小李和小王都映射到了索引下标 1 的位置。如图所示,小李和小王都映射到了索引下标 1 的位置。
一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
拉链法
拉链法解决冲突的做法是:
将所有关键字为同义词的结点链接在同一个单链表中。
线性探测法
线性探测法是最简单的开放地址法,线性探测的增量序列为di =1,…,m -1。
例如,有一组关键字(14,36,42,38,40,15,19,12,51,65,34,25),若表长为15,散列函数为hash(key)=key%13,则可采用线性探测法处理冲突,构造该散列表。
常见的三种哈希结构
- 数组
- set (集合)
- map(映射)
1.当元素个数较少并且能知道大概元素个数时,使用数组
2.当元素数量较大时,使用set
3.当一个数据涉及两种因素时(即key和value),使用map
std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
std::map: key有序
std::multimap: key有序 且 有重复数据的时候
std::unordered_map: 优先考虑这个,因为他的增删和查找效率最高
-
242.有效的字母异位词
题目:
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1: 输入: s = "anagram", t = "nagaram" 输出: true
示例 2: 输入: s = "rat", t = "car" 输出: false
思路:
首先定义一个数组用来记录字符串s里字符出现的次数,然后需要把字符映射到数组也就是哈希表的索引下标上,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。表示为 s[i] - ‘a’ , 然后遍历数组,统计次数。
检查字符串t中是否出现了这些字符,通过遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。
最后检查record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。
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;
}
};
-
349. 两个数组的交集
题目:
给定两个数组,编写一个函数来计算它们的交集。
思路:
本题没有限制数值的大小,所以无法使用数组来做哈希表。
而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
此时就要使用另一种结构体了——set 。
使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set;
unordered_set<int> nums_set(nums1.begin(), nums1.end());
for (int num : nums2) {
if (nums_set.find(num) != nums_set.end()) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
-
202. 快乐数
题目:
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
思路:
本题要快速判断一个元素是否出现集合里,考虑哈希法判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
判断sum是否重复出现可以使用unordered_set。
class Solution {
public:
// 取数值各个位上的单数之和
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> set;
while(1) {
int sum = getSum(n);
if (sum == 1) {
return true;
}
if (set.find(sum) != set.end()) {
return false;
} else {
set.insert(sum);
}
n = sum;
}
}
};