一、哈希表的模拟实现
这里选用的哈希函数就是除留余数法
案例:题目描述:
输入描述:
测试用例:121 11 21 31 41 52 21 62 51 72 82 42 25
1.1 线性探测法
1.1.1 创建
一般来说 , 假设数据个数为 n , 我们会创建数组大小为 大于2n 的第一个质数 (因为如果数据密集,存储空间少 , 会出现多次探测)
#include <iostream>
#include <string>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 23;
//哈希表
int h[N];
void init()
{
memset(h,0x3f,sizeof h);
}
int main()
{
init();
return 0;
}
1.1.2 哈希函数以及处理哈希冲突
除留余数法: hash(key) = key % N但是要注意, key 有可能是负数,取模之后会变成负数
- 负数补正的操作为:加上模数即可
- 但是正数加上模数会变大,所以统一再取一次模
//哈希函数
//f(x) 返回 x 的映射地址
int f(int x)
{
//模 加 模
int id = (x%N+N) % N;
//处理冲突
while(h[id]!=INF && id!=x)
{
id++;
if(id == N)id = 0;//走到头了,拐个弯
}
return id;
}
1.1.3 添加元素
通过哈希函数找到合适的位置 , 然后放上取即可
//插入元素
void insert(int x)
{
//通过哈希函数找到存储位置
int id = f(x);
h[id] = x;
}
1.1.4 查找元素
通过哈希函数找到映射位置 , 看看里面是不是 x
//查找元素
void find(int x)
{
int id = f(x);
return h[id] == x;
}
1.1.5 所有测试代码
#include <iostream>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 23;
int n;
//哈希表
int h[N];
void init()
{
memset(h,INF,sizeof h);
}
//哈希函数
//f(x) 返回 x 的映射地址
int f(int x)
{
//模 加 模
int id = (x%N+N) % N;
//处理冲突
while(h[id]!=INF && id!=x)
{
id++;
if(id == N)id = 0;//走到头了,拐个弯
}
return id;
}
//插入元素
void insert(int x)
{
//通过哈希函数找到存储位置
int id = f(x);
h[id] = x;
}
//查找元素
bool find(int x)
{
int id = f(x);
return h[id] == x;
}
int main()
{
init();
cin >> n;
while(n--)
{
int op,x;
cin >> op >> x;
if(op == 1)
{
insert(x);
}
else
{
if(find(x)) cout << "yes" << endl;
else cout << "no" << endl;
}
}
return 0;
}
这里就不介绍删除元素了,很复杂~
1.2 链地址法
1.2.1 创建
实现方式与树的链式向前星一样~~~
本质就是用数组模拟链表
#include <iostream>
#include <cstring>
using namespace std;
const int N = 23;
int h[N];//哈希表
int e[N],ne[N],id;
int main()
{
return 0;
}
1.2.2 哈希函数
//哈希函数
int f(int x)
{
//模 加 模
return (x%N + N) %N;
}
1.2.3 添加元素以及处理哈希冲突
//添加元素以及处理哈希冲突
void insert(int x)
{
int idx = f(x);
//把x 头插到idx 所在的链表中
id++;
e[id] = x;
ne[id] = h[idx];
h[idx] = id;
}
1.2.4 查找元素
1)先计算 x 对应的哈希值
2)在哈希值所在的链表中查找
//查找元素
bool find(int x)
{
int idx = f(x);
for(int i = h[idx];i;i = ne[i])
{
if(e[i] == x)return true;
}
return false;
}
1.2.5 所有测试代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 23;
int h[N];//哈希表
int e[N],ne[N],id;
//哈希函数
int f(int x)
{
//模 加 模
return (x%N + N) %N;
}
//插入元素 - 处理哈希冲突
void insert(int x)
{
int idx = f(x);
//把x 头插到idx 所在的链表中
id++;
e[id] = x;
ne[id] = h[idx];
h[idx] = id;
}
//查找元素
bool find(int x)
{
int idx = f(x);
for(int i = h[idx];i;i = ne[i])
{
if(e[i] == x)return true;
}
return false;
}
int main()
{
int n;
cin >> n;
while(n--)
{
int op,x;
cin >> op >> x;
if(op == 1)
{
//插入元素
insert(x);
}
else//查询
{
if(find(x))cout << "yes" << endl;
else cout << "no" << endl;
}
}
return 0;
}
二、unordered set / unordered_multiset
set 与 unordered_set 的区别就是 , 前者是用红黑树实现的 , 后者是用哈希表是实现的。
使用方式是完全一样的。无非就是存储和查找的效率不一样 , 以及前者存的是有序 , 后者存的是无序 。
2.1 创建unordered set
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<int> mp1;
unordered_set<string> mp2;
return 0;
}
2.2 size / empty
size : 求哈希表内实际的元素个数
empty : 判断哈希表是否为空
时间复杂度:O(1)
2.3 begin /end
迭代器 , 可以使用范围 for 遍历哈希表中的所有元素
但是需要注意 , 哈希表不等同于红黑树 , 遍历出来的结果是无序的
2.4 insert
往哈希表中插入一个元素
时间复杂度 可以近似认为 : O(1)
2.5 erase
在哈希表中删除一个元素
时间复杂度 可以近似认为 : O(1)
2.6 find/count
1)find : 查找一个元素 , 返回的是迭代器
2)count : 查询元素出现的次数 , 一般用来判断元素是否在哈希表中
时间复杂度 可以近似认为 : O(1)
2.7 所有测试代码
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
string str[] = {"张三","李四","王五","赵六"};
unordered_set<string> mp;
for(auto& x:str)
{
mp.insert(x);
}
if(mp.count("张三") ) cout << "yes" << endl;
else cout << "no" << endl;
mp.erase("张三");
if(mp.count("张三") ) cout << "yes" << endl;
else cout << "no" << endl;
for(auto& x:mp )
{
cout << x << " ";
}
return 0;
}
三、unordered map / unordered_multimap
3.1 创建unordered map
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int main()
{
unordered_map<int,int> mp1;
unordered_map<string,int> mp1;
unordered_map<int,string> mp1;
unordered_map<int,vector<int>> mp1;
//这个是不是它可以 存图
return 0;
}
3.2 size / empty
size : 求哈希表内实际的元素个数
empty : 判断哈希表是否为空
时间复杂度:O(1)
3.3 begin /end
迭代器 , 可以使用范围 for 遍历哈希表中的所有元素
3.4 insert
往哈希表中插入一个元素
时间复杂度 可以近似认为 : O(1)
3.5 operator[ ]
重载[ ] , 使 unordered_map 可以像数组一样使用
3.6 erase
在哈希表中删除一个元素
时间复杂度 可以近似认为 : O(1)
3.7 find/count
1)find : 查找一个元素 , 返回的是迭代器
2)count : 查询元素出现的次数 , 一般用来判断元素是否在哈希表中
时间复杂度 可以近似认为 : O(1)
3.8 所有测试代码
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
void test()
{
unordered_map<int, vector<int>> mp;
mp[1].push_back(2);
mp[2] = {3, 4, 5};
mp[3].push_back(1);
mp[3].push_back(2);
mp[3].push_back(3);
for(auto& [a, v] : mp)
{
cout << a << ": ";
for(auto b : v) cout << b << " ";
cout << endl;
}
}
int main()
{
test();
unordered_map<string, int> mp;
// 写一个大括号,把需要放进去的元素括起来即可
mp.insert({"张三", 1});
mp.insert({"李四", 2});
mp.insert({"王五", 3});
// operator[]:可以让 map 像数组一样使用
// 赋值
mp["赵六"] = 4; // 相当于往 mp里面插入一个 <"赵六", 4>
// 查询里面的值
if(mp["赵六"] == 4) cout << "yes" << endl;
else cout << "no" << endl;
// 使用查询的时候要注意,如果 map 中本身没有该元素,它会先插入,然后再拿值
// 插入的时候,第1个关键字是默认值
// 如果是数,那就是 0
// 如果是字符串,那就是空串
if(mp["小美"]) cout << "no" << endl; // 会把 <"小美", 0> 放进去
if(mp.count("小帅")) cout << "yes" << endl;
else cout << "no" << endl;
mp.erase("小美");
for(auto& [s, num] : mp)
{
cout << s << "编号为: " << num << endl;
}
return 0;
}
四、哈希表算法题
4.1 学籍管理
#include <iostream>
#include <vector>
#include <unordered_map>
typedef long long LL;
using namespace std;
int n;
unordered_map<string,int> mp;
int main()
{
cin >> n;
while(n--)
{
int op;
string name;
LL score;
cin >> op ;
if(op == 1)
{
cin >> name >> score;
mp[name] = score;
cout << "OK" << endl;
}
else if(op == 2)
{
cin >> name;
if(mp.count(name) )
{
cout << mp[name] << endl;
}
else
cout << "Not found" << endl;
}
else if(op == 3)
{
cin >> name;
if(mp.count(name) )
{
mp.erase(name);
cout << "Deleted successfully" << endl;
}
else{
cout << "Not found" << endl;
}
}
else
{
cout << mp.size() << endl;
}
}
return 0;
}
4.2 不重复数字
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int main()
{
int n;
cin >> n;
while(n--)
{
int p;
unordered_set<int> mp;
cin >> p;
while(p--)
{
int x;
cin >> x;
if(!mp.count(x))//是第一次出现
{
cout << x << " ";
mp.insert(x);
}
}
cout << endl;
}
return 0;
}
处理以下 cin 和 cout
#include <iostream>
#include <vector>
#include <unordered_set>
#include <cstdio>
using namespace std;
int main()
{
int n;
//cin >> n;
scanf("%d",&n);
while(n--)
{
int p;
unordered_set<int> mp;
//cin >> p;
scanf("%d",&p);
while(p--)
{
int x;
scanf("%d",&x);
if(!mp.count(x))//是第一次出现
{
//cout << x << " ";
printf("%d ",x);
mp.insert(x);
}
}
// cout << endl;
puts(" ");
}
return 0;
}
4.3 阅读理解
#include <iostream>
#include <set>
#include <unordered_map>
using namespace std;
//标记单词在那些文章出现过
unordered_map<string,set<int>> mp;
int main()
{
int n;
cin >> n;
for(int i = 1;i<=n;i++)
{
int l;cin >> l;
while(l--)
{
string s;
cin >> s;
mp[s].insert(i) ;
}
}
int m;
cin >> m;
while(m--)
{
string s;
cin >> s;
for(auto e:mp[s])
{
cout << e << " ";
}
cout << endl;
}
return 0;
}
4.4 A-B数对
#include <iostream>
#include <set>
#include <unordered_map>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
LL n,c;
LL a[N];
unordered_map<int,int> mp;
int main()
{
cin >> n >> c;
for(int i = 1;i<=n;i++)
{
cin >> a[i];
mp[a[i]]++;
}
LL ret = 0;
for(int i = 1;i<=n;i++)
{
//b = a[i]
//c + a[i]
ret += mp[c + a[i]];
}
cout << ret << endl;
return 0;
}
4.5 Cities and States
P3405 [USACO16DEC] Cities and States S - 洛谷
#include <iostream>
#include <unordered_map>
using namespace std;
//<拼接后的对应关系,次数>
unordered_map<string,int> mp;
int main()
{
int n;
cin >> n;
int ret = 0;
while(n--)
{
string a,b;
cin >> a >> b;
a = a.substr(0,2);
if( a == b ) continue;
ret += mp[b+a];//统计b->a有多少个
mp[a+b]++;
}
cout << ret << endl;
return 0;
}