拉链法:
用数组h[N]来表示拉链法上对应的链,如果遇到冲突则在对应冲突的位置开一个链,创建链的方式和之前单链表的方式相同。如果要插入一个值:
- 计算当前值在哈希之后的映射位置
int k = (x % N + N) % N;
之所以要取两次模,是为了处理负数的模 - 将x存储在e[idx]中
- 该链表的下一个位置就是当前冲突位置的链表的头,故
ne[idx] = h[k];
- 当前冲突位置链表的头变为了当前的idx
h[k] = idx++;
开放选址法
只需要一个一维数组用来存储。
在插入时遇到冲突,按数组顺序向后查找,直到找到空位置将其插入。
在查找时,从哈希映射的位置开始查找,如果找到这个值,则返回其下标,如果直到当前位置为空时,还没有找到,则说明该值不存在。
取余的数应该是一个质数,并且应该大于题目要求的两倍。
问题描述
维护一个集合,支持如下几种操作:
“I x”,插入一个数x;
“Q x”,询问数x是否在集合中出现过;
现在要进行N次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数N,表示操作数量。
接下来N行,每行包含一个操作指令,操作指令为”I x”,”Q x”中的一种。
输出格式
对于每个询问指令“Q x”,输出一个询问结果,如果x在集合中出现过,则输出“Yes”,否则输出“No”。
每个结果占一行。
数据范围
1≤N≤105
−109≤x≤109
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No
代码1
// 拉链法
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100003;
int h[N], e[N], ne[N], idx = 0;
void insert(int x){
int k = (x % N + N) % N;
e[idx] = x;
ne[idx] = h[k];
h[k] = idx++;
}
bool find(int x){
int k = (x % N + N) % N;
for(int i = h[k]; i != -1; i = ne[i]){
if(e[i] == x){
return true;
}
}
return false;
}
int main(){
int m;
memset(h, -1, sizeof h);
cin >> m;
while(m --){
string s;
int x;
cin >> s >> x;
if(s == "I"){
insert(x);
}else{
if(find(x)){
cout << "Yes" << endl;
}else{
cout << "No" << endl;
}
}
}
return 0;
}
代码2
#include<iostream>
#include<cstring>
using namespace std;
const int N = 200003;
int null = 0x3f3f3f3f;
int h[N];
int find(int x){
int k = (x % N + N) % N;
for(int i = k; ; ++i){
if(h[i] == null){
return i;
}
if(h[i] == x){
return i;
}
}
return k;
}
int main(){
int m;
memset(h, null, sizeof h);
cin >> m;
while(m --){
string s;
int x;
cin >> s >> x;
if(s == "I"){
int k = find(x);
h[k] = x;
}else{
if(h[find(x)] != null){
cout << "Yes" << endl;
}else{
cout << "No" << endl;
}
}
}
return 0;
}