维护一个集合,支持如下几种操作:
I x
,插入一个整数 x;Q x
,询问整数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N,表示操作数量。
接下来 N 行,每行包含一个操作指令,操作指令为 I x
,Q x
中的一种。
输出格式
对于每个询问指令 Q x
,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
1≤N≤10^5
−10^9≤x≤10^9
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No
思路:要把范围这么大的X存到集合中,直接放进数组不合适,所以就要把大范围映射到小范围------>哈希表
哈希表
本题使用拉链法,也就是哈希表一个位置上有一条链表,用来存取模后映射到小范围上相等的数字(避免冲突),实现两个功能,插入和查找
其中取模要用到这个公式 ,保证结果是正数(哈希表范围从0到N)
int k=(x%N+N)%N;
拉链法需要注明的点:
哈希表上每个点对应一条链表,用来存取模后映射到小范围上相等的数字(避免冲突),h[]表示哈希表,h[k]表示哈希表中第k个位置上的值,h[k]存的是它这个位置上对应的链表的头结点下标
看图比较好理解:
插入部分:
void insert(int x)
{
//h[k]是链表k的第一个元素在e数组中的索引
int k=(x%N+N)%N; //把它映射为0-N的一个正数
e[idx]=x; //赋值
ne[idx]=h[k]; //当前节点的下一个位置是原来的头结点(下标)
h[k]=idx; //现在头节点是当前节点(这里是下标)
idx++; //自增,之后会继续操作
}
查找部分:
bool find(int x)
{
int k=(x%N+N)%N;
for(int i=h[k];i!=-1;i=ne[i]) //从头结点开始,到-1(没有节点了),i向后遍历
{
if(e[i]==x) //如果在这个哈希表上的某条链表里找到了值
return true;
}
return false;
}
puts()函数用来向标准输出设备屏幕输出字符串并换行。
示例代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx; //h[N]是表,e和ne表示拉链这个链表中的值和下一个点下标
/*h[]是一个整数数组,用于表示哈希表。
h[k]表示哈希表中第k个槽位的值。
当h[k]的值为-1时,表示第k个槽位为空,即对应的链表为空。
当h[k]的值不为-1时,表示第k个槽位存储了链表的头结点下标。它指向链表中第一个元素的位置。
*/
void insert(int x)
{
//h[k]是链表k的第一个元素在e数组中的索引
int k=(x%N+N)%N; //把它映射为0-N的一个正数
e[idx]=x; //赋值
ne[idx]=h[k]; //当前节点的下一个位置是原来的头结点(下标)
h[k]=idx; //现在头节点是当前节点(这里是下标)
idx++; //自增,之后会继续操作
}
bool find(int x)
{
int k=(x%N+N)%N;
for(int i=h[k];i!=-1;i=ne[i]) //从头结点开始,到-1(没有节点了),i向后遍历
{
if(e[i]==x) //如果在这个哈希表上的某条链表里找到了值
return true;
}
return false;
}
int main()
{
int n;
scanf("%d",&n);
memset(h,-1,sizeof(h)); //初始化哈希表,把每个位上都赋为-1,表示当前位上对应的链表里面没有有效节点
while(n--)
{
char op[2];
int x;
scanf("%s%d",op,&x);
if(*op=='I') insert(x); //指针指向数组的首元素
else
{
if(find(x)) puts("Yes"); //输出字符串并换行
else puts("No");
}
}
return 0;
}