一、哈希表基础
哈希表(Hash Table)也称散列表,是根据关键码值(key, value)而直接进行访问的数据结构。
如何将键值映射到哈希表上? => 使用哈希函数
1、将键值映射为哈希表上具体的索引数字:利用hashCode将键值转化为具体数值(hashcode通过特定编码方式,可以将其他数据格式转化为不同的数值)
2、若hashCode得到的数值大于哈希表的大小:将得到的索引数字对哈希表的大小取模,保证键值一定可以映射到哈希表上。
3、若有不同的键值映射到了同一个索引数值,则产生了哈希冲突。
其中简化代码如下:
//哈希函数:hash_function
index_value=hash_function(key); //key是键值
int hash_function(int key,int hash_size){
return hash_code(key)%hash_size; //hash_size代表哈希表大小
}
解决哈希冲突的方法
主要有两种方法:开放地址法和链地址法(也叫拉链法)
1、开放地址法
用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测算法在散列表中寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到。按照探测序列的方法,一般将开放地址法区分为线性探查法、二次探查法、双重散列法等。(要求hash_size一定要大于data_size,要不然哈希表上就没有空置的位置来存放冲突的数据了)
2、拉链法
用拉链法解决冲突的做法是:当冲突发生时,将发生冲突的元素(键值)存储在链表中,可以通过索引找到发生冲突的元素。需要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。

c++中哈希表的实现
在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:


二、unordered_map基本操作
int main()
{
//哈希表的初始化及插入
unordered_map<int, string> hash_map={{ 1, "熊大" },{ 2, "熊二" }};//使用{}赋值
hash_map[3] = "光头强"; //使用[ ]进行单个插入,若已存在键值3,则赋值修改,若无则插入。
hash_map.insert(pair<int, string>(4, "拖拉机"));//使用insert和pair插入
hash_map.emplace(pair<int, string>(5, "树"));//效率比 insert() 方法高
//常见操作
if(hash_map.find(2)!=hash_map.end()){
cout<<"找到键值为2的值为"<<hash_map.at(2)<<endl;
}
hash_map.erase(2);//删除键值为2的成员
cout << "hash_map size==" << hash_map.size() << endl;//输出hash_map大小
//遍历输出
for(auto it=hash_map.begin();it!=hash_map.end();it++){
cout<<it->first<<" "<<it->second<<endl;
}
getchar();
return 0;
}
三、哈希表常见力扣题
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char>res;
int left=0,right=0;
int length=0;
while(right<s.size()){
while(res.find(s[right])!=res.end()){//保证滑动窗口内没有重复字母
res.erase(s[left]);
left++;
}
res.insert(s[right]);
length=max(length,right-left+1);
right++;
}
return length;
}
};
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> combinations;
if(digits.empty()){
return combinations;
}
unordered_map<char,string> phonemap={
{'2',"abc"},
{'3',"def"},
{'4',"ghi"},
{'5',"jkl"},
{'6',"mno"},
{'7',"pqrs"},
{'8',"tuv"},
{'9',"wxyz"}
};
string combination;
backtrace(phonemap,combinations,digits,0,combination);
return combinations;
}
void backtrace(const unordered_map<char,string>& phonemap,vector<string>& combinations,string& digits,int index,string& combination){
if(index==digits.length())
combinations.push_back(combination);
else{
char digit=digits[index];
const string& letters=phonemap.at(digit);
for(const char& letter:letters){
combination.push_back(letter);
backtrace(phonemap,combinations,digits,index+1,combination);
combination.pop_back();
}
}
}
};
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> hashmap;
for(string& str:strs){
string key=str;
sort(key.begin(),key.end());
hashmap[key].emplace_back(str);
}
vector<vector<string>> res;
for(auto it=hashmap.begin();it!=hashmap.end();it++){
res.emplace_back(it->second);
}
return res;
}
};
class Solution {
public:
unordered_map <char, int> ori, cnt;
bool check(){
for(const auto &it:ori){
if(cnt[it.first]<it.second)
return false;
}
return true;
}
string minWindow(string s, string t) {
if(t.size()>s.size()) return {};
int left=0,right=-1; //注意right初始化为-1
int left_out=-1;
int length=INT_MAX;
string out={};
for(const auto &it:t){
ori[it]++;
}
while(right<int(s.size())){ //注意int类型转换,因为s.size()类型为unsigned int,right初始化为负数
right++;
if(ori.find(s[right])!=ori.end()){
cnt[s[right]]++;
}
while(left<=right&&check()){
if((right-left+1)<length){
length=right-left+1; //记录最小串长度
left_out=left; //记录最小串起始位置
}
if(ori.find(s[left])!=ori.end()){
cnt[s[left]]--;
}
left++;
}
}
//cout<<s[right];
return left_out==-1?out:s.substr(left_out,length);
}
};
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> hash;
for(auto x:nums) hash.insert(x);
int res=0;
for(auto x:hash){
if(!hash.count(x-1)){//找到最小的那个
int y=x;
while(hash.count(y+1))y++; //找最大的那个
res=max(res,y-x+1);
}
}
return res;
}
};


被折叠的 条评论
为什么被折叠?



