前言:此处开始使用哈希表,一般哈希表都是用来快速判断一个元素是否出现集合里
- 哈希函数:一种映射关系,将元素映射到哈希表
- 哈希碰撞:哈希表中多种元素都映射到同一个位置,解决方法:拉链法(链表的方式),线性探测法(找到哈希表中空闲的位置)
数据结构:
- 数组
- set
- map
- 哈希值连续或较近,范围可控,使用数组
- 范围比较大,使用set
- key与value对应,使用map
使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset
比较:
第一题 242.有效的字母异位词
数组(简单哈希表):
本题的范围是26个小写字母,因此定义一个hash[26]用于存放每个字母的个数,第一个字符串出现的字母在对应位置加一,第二个字符串减一,遍历之后判断整个hash[26]是否为{0,0,0....}如果是则是异位词
class Solution {
public:
bool isAnagram(string s, string t) {
int hash[26] = {0};
/* for (int i = 0; i < 26; i++) {
hash[i] = 0;
}*/
for (int i = 0; i < s.size(); i++) {
hash[s[i] - 'a']++;
}
for (int j = 0; j < t.size(); j++) {
hash[t[j] - 'a']--;
}
int count = 0;
for (int k = 0; k < 26; k++) {
if (hash[k] == 0)count++;
}
if (count == 26) {
return true;
}
return false;
}
};
第二题 349. 两个数组的交集
使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,因此此题选择unordered_set
注意判断一个元素nums2[i]是否在哈希表中,应该用“if(nums1_set.find(nums2[i])!=nums1_set.end())”因为find函数返回的是迭代器的位置
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
//存放结果
unordered_set<int> result;
//存放去重后的第一个数组
unordered_set<int> nums1_set(nums1.begin(), nums1.end());
//把第二个数组的每一个元素和nums1_set比较
for (int i = 0; i < nums2.size();i++) {
//出现过
if (nums1_set.find(nums2[i])!=nums1_set.end()) {
result.insert(nums2[i]);
}
}
return vector<int>(result.begin(), result.end());
}
};
第三题 202. 快乐数
因为可能出现无限循环,因此需要查找当前的平方和是不是在前面出现过,即判断一个元素(当前平方和)是否在集合(以前所有的的平方和)里,考虑使用哈希表,又因为不需要重复不需要有序,所以使用unordered_set
整个题的思路是先把n的各个位分离,求各个位的平方和sum,如果sum=1则是快乐数,结束程序,如果sum!=1则:
- 如果sum不在set中,把sum存到哈希表set中(unordered_set自动去重),重复操作
- 如果sum在set中,说明出现循环,不可能再出现和为1的情况,结束程序
由上述例子得出,要求一个数的每一个位数,先%10得到位数,再/10循环
class Solution {
public:
bool isHappy(int n) {
unordered_set<unsigned int>set;
while (n) {
//先把各个位分离
unsigned int sum = 0;
while (n != 0) {
sum += (n % 10) * (n % 10);
n = n / 10;
}
//是快乐数
if (sum == 1)return true;
//没有在set找到sum
if (set.find(sum) == set.end()) {
set.insert(sum);
}
//在set中找到了sum,进入循环
else {
return false;
}
n = sum;
}
return false;
}
};
第四题 1. 两数之和
本题由于需要返回下标,key(存元素)和value(存下标)都需要,所以需要用map,用于快速查找key是否在map中出现过,所以key用于存元素,value用于存下标
问题:我原本的想法是使用multimap将原来的数组存入,因为本题的数组元素是可以重复的,但问题是multimap是有序的,会导致原来数组的顺序发生变化,因此不可以用
视频思路
定义一个unordered_map用于存放遍历过的元素,在map中查找target-nums[i],如果找到了则返回i和map的value值,如果没有找到则将nums[i]存放在map中,重复操作,最后如果没有找到则返回空
注意:
- 求value值用“auto iter= map.find(target - nums[i]);”此时“iter->second”就是value值
- map中插入时用“map.insert(pair<int,int>(nums[i],i));”
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//存放遍历过的元素
unordered_map<int, int>map;
for (int i = 0; i < nums.size(); i++) {
//迭代器
auto iter= map.find(target - nums[i]);
//找到了匹配的
if ( iter!= map.end()) {
return { i,iter->second };
}
else {
map.insert(pair<int,int>(nums[i],i));
}
}
return {};
}
};