哈希表理论基础
1、重要的事情放在开头,当看到需要在一个元素集合中查询是否存在某一个元素时,就应想到使用哈希法。
2、常见的哈希结构包括数组,set,map,数据本质上就是一个哈希表,哈希表就是利用关键码来访问数据,数组即是利用索引直接访问到数据值。
3、将不同数据类型的数据转换成关键码就用到了哈希函数,那么如果数据大小大于哈希表大小,导致存放数据间发生冲突,即哈希碰撞,应该怎么办呢,这时候考虑用拉链法,即在碰撞的位置存放一张链表来存放发生碰撞的数据;那如果可以保证数据大小是小于哈希表大小的,那可以使用线性探测法,当发生碰撞时,向下寻找下一个空位来存放发生碰撞的数据。
4、关于set(map)、multiset(map)、unordered_set(map)的特点如下表所示
当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。
在map 是一个key value 的数据结构,map中,对key是有限制,对value没有限制的,因为key的存储方式使用红黑树(二叉平衡搜索树)实现的。map中虽然查询底层是红黑树,但对于我们还是hash来实现的,即通过关键值得方式来查找值。
综上所述,哈希法是用空间换取了时间,创建了一个额外的set或者map来存放数据
LeetCode242有效的字母异位词
该题目判断两个字符串是不是含有相同的字母(顺序可以不同),考虑到只包含a-z26个字符,数目较小,可以利用数组来实现哈希操作,维护一个长度为26得数组,利用ASCII码的减法获取数组下标,代码如下:
时间复杂度O(n);空间复杂度O(1);
class Solution {
public:
bool isAnagram(string s, string t) {
int hash[26]={0};
int a=s.size();
for(int i=0;i<s.size();i++) hash[s[i]-'a']++;
for(int j=0;j<t.size();j++) hash[t[j]-'a']--;
for(int k=0;k<sizeof(hash)/sizeof(hash[0]);k++){
if(hash[k]!=0) return false;
}
return true;
}
};
LeetCode349两个数组的交集
利用set:
注意:1、set的初始化可以直接通过vector转换;vector也可以直接通过set的迭代器转换;2、如果set未找到数值返回end();
时间复杂度(n+m) , m是要把set转换成vector;空间复杂度O(n)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// 利用set
unordered_set<int> res_set;
unordered_set<int> nums1_set(nums1.begin(),nums1.end());
for(int num : nums2){
if(nums1_set.find(num)!=nums1_set.end()){
res_set.insert(num);
}
}
return vector<int>(res_set.begin(),res_set.end());
}
};
利用数组:
时间复杂度(n+m) , m是要把set转换成vector;空间复杂度O(n)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
//利用数组
int hash[1001]={0};
unordered_set<int> res;
for(int i=0;i<nums1.size();i++){
hash[nums1[i]]=1;
}
for(int j=0;j<nums2.size();j++){
if(hash[nums2[j]]==1){
res.insert(nums2[j]);
}
}
return vector<int>(res.begin(),res.end());
}
};
LeetCode202快乐数
每次计算得到的结果存到哈希表中,如果计算结果等于1则返回true,如果计算结果已经存在于哈希表中,则返回false、
class Solution {
public:
int get_num(int n){
int sum=0;
while(n){
sum += (n%10)*(n%10);
n = n/10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> res_set;
while(get_num(n)!=1){
if(res_set.find(get_num(n))!=res_set.end()) return false;
res_set.insert(get_num(n));
n = get_num(n);
}
return true;
}
};
LeetCode1两数之和
根据数组值来判断是否两个数加和=target,返回下标。
本题需要注意的点在于:1、要想到用哈希法,当遍历数组元素的时候,需要判断target-元素值是不是曾经遍历过,因此想到用哈希法;
2、要返回下标索引,因此要想到用键值对的方式,数组值做键,下标做值(前提无重复元素)
3、pair<int,int>的用法,包括键值对的成员变量first,second要掌握。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> res_map;
for(int i=0;i<nums.size();i++){
if(res_map.find(target-nums[i])!=res_map.end()){
return {res_map[target-nums[i]],i};
// return {res_map.find(target-nums[i])->second,i};
}
res_map[nums[i]]=i;
// res_map.insert(pair<int,int>(nums[i],i));
}
return {};
}
};