维护一个集合,支持如下几种操作:
I x
,插入一个数 x;Q x
,询问数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N,表示操作数量。
接下来 N 行,每行包含一个操作指令,操作指令为 I x
,Q x
中的一种。
输出格式
对于每个询问指令 Q x
,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
1≤N≤1e5
−1e9≤x≤1e9
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No
代码如下:
第一种方法(拉链法):
单链表的使用:22. 单链表(c++)_AMG GTR的博客-CSDN博客
#include<iostream>
#include<cstring>
using namespace std;
const int N=100003;//哈希取模的数尽量取质数(由数学证明这样误差最小)
int h[N],e[N],ne[N],idx;//单链表的应用
void insert(int x)//插入操作;通过链表来存储哈希过后一一映射的位置。
{
int k=(x%N+N)%N;//c++中如果是负数那他取模也是负的,所以加N再%N就一定是一个正数
e[idx]=x;
ne[idx]=h[k];
h[k]=idx++;
}
bool find(int x)//在链表当中寻找是否存在一个结点对应等于所找的x值
{
int k=(x%N+N)%N;//c++中如果是负数那他取模也是负的,所以加N再%N就一定是一个正数
for(int i=h[k];i!=-1;i=ne[i])//走到空指针(-1)就停止
if(e[i]==x)
return true;//循环查找后如果找到就标记为对(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[0]=='I') insert(x);
else
{
if(find(x)) puts("Yes");
else puts("No");
}
}
return 0;
}
第二种方法(开放寻址法):
#include <iostream> #include <cstring>//使用memset得使用这个头文件 using namespace std;//开放寻址法一般开 数据范围的 2~3倍, 这样大概率就没有冲突了 const int N = 2e5 + 3; //取大于数据范围的第一个质数 const int null = 0x3f3f3f3f; //规定空指针为 null 0x3f3f3f3f int h[N]; int find(int x) { //这里是用x去模 int t = (x % N + N) % N;//c++中如果是负数那他取模也是负的,所以加N再%N就一定是一个正数 while (h[t] != null && h[t] != x)//一个一个依次的寻找 { t++;//寻找下一位 if (t == N) t = 0; } //记得要return t; return t; //如果这个位置是空的, 则返回的是他应该存储的位置 } int main() { int n; cin >> n; memset(h, null, sizeof h); //规定空指针为 0x3f3f3f3f while (n--) { string op;//利用string来读入数组。 int x; cin >> op >> x; if (op == "I") h[find(x)] = x;//插入,因为如果不存在这个数在 h[t] != null会跳出循环,从而把原来空的位置将x放入这个链表 //如果存在这个数在h[t] != x时也会跳出循环,这个链表仍然包含了x else { //如果没找到,h[t这个位置]是空的; if (h[find(x)] == null) puts("No");//通过find函数来实现; else puts("Yes"); } } return 0; }