模拟散列表
例题:
维护一个集合,支持如下几种操作:
“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
一、拉链法
哈
希
函
数
的
模
数
通
常
取
大
于
数
据
总
量
的
第
一
个
质
数
。
这
里
取
100003
,
是
大
于
100000
的
第
一
个
质
数
。
哈希函数的模数通常取大于数据总量的第一个质数。\\这里取100003,是大于100000的第一个质数。
哈希函数的模数通常取大于数据总量的第一个质数。这里取100003,是大于100000的第一个质数。
模板:
#include <cstring>
#include <iostream>
using namespace std;
const int N=100003; //大于100000第一个质数
int h[N],e[N],ne[N],idx;
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 ;i=ne[i])
if(e[i]==x) return true;
return false;
}
int main()
{
int n;
cin>>n;
memset(h,-1,sizeof h);
while(n--)
{
char op[2];
int x;
cin>>op>>x;
if(*op=='I') Insert(x);
else
{
if(Find(x)) puts("Yes");
else puts("No");
}
}
return 0;
}
二、开放地址法
只
开
一
个
数
组
,
映
射
的
区
间
通
常
是
数
据
总
量
的
两
到
三
倍
,
模
数
同
样
取
大
于
区
间
范
围
的
第
一
个
质
数
。
这
里
取
200003
,
是
大
于
200000
的
第
一
个
质
数
。
另
外
,
需
要
指
定
一
个
表
示
查
找
失
败
的
值
,
这
个
值
必
须
在
数
组
范
围
之
外
。
这
里
取
0
x
3
f
3
f
3
f
3
f
在
1
0
9
之
外
。
只开一个数组,映射的区间通常是数据总量的两到三倍,模数同样取大于区间范围的第一个质数。\\这里取200003,是大于200000的第一个质数。\\另外,需要指定一个表示查找失败的值,这个值必须在数组范围之外。这里取0x3f3f3f3f在10^9之外。
只开一个数组,映射的区间通常是数据总量的两到三倍,模数同样取大于区间范围的第一个质数。这里取200003,是大于200000的第一个质数。另外,需要指定一个表示查找失败的值,这个值必须在数组范围之外。这里取0x3f3f3f3f在109之外。
模板:
#include <cstring>
#include <iostream>
using namespace std;
const int N=200003; //大于200000第一个质数
const int Null=0x3f3f3f3f;
int h[N];
int Find(int x) //若存在,返回所在下标;若不存在,返回应该插入的下标
{
int k=(x%N+N)%N;
while(h[k]!=Null&&h[k]!=x)
{
k++;
if(k==N) k=0; //从头开始循环
}
return k;
}
int main()
{
int n;
cin>>n;
memset(h,0x3f,sizeof h);
while(n--)
{
char op[2];
int x;
cin>>op>>x;
int k=Find(x); //映射下标
if(*op=='I') h[k]=x;
else
{
if(h[k]!=Null) puts("Yes");
else puts("No");
}
}
return 0;
}