这里将会引入一种哈希表的数据结构,粗看之下我们会觉得这个的形式特别像我们之前学过的离散化,就是将一个极大的值域映射到一个相对小的空间中以便我们的查询和删除。但是值得注意的是,离散化保序而哈希表不保序。就比如我们之前学到的离散化的求区间和,这个是需要保存其序列的,我们是对应的去求其某一段范围之内的和,所以说离散化,其实是一种特殊的哈希表;
然后我们先介绍一下哈希表的一些具体操作,我会定义一个Hash函数(其实就是直接让这个x去模取一个定值),for example(x % maxn +maxn)%maxn,然后我们就要去先想办法去存储值和查询值,我们有两种方式来解决它:分别是开放寻址法和拉链法;
###开放寻址法
#include<bits/stdc++.h>
#define maxn 200003 //通常情况下,开放寻址因为要避免更大的冲突会取大两到三倍,并取第一个质数
using namespace std;
int null = 0x3f3f3f3f,h[maxn]; //因为这个null的值是不在x的范围之内的,所以可以起到一个标识作用
int find(int x)
{
int k = (x % maxn + maxn)%maxn;
while(h[k]!=null && h[k]!=x){ //去找到第一个空坑位
k++;
if(k==maxn) k=0;
}
return k;
}
int main()
{
memset(h,0x3f3f3f,sizeof(h));
int n;
cin>>n;
while(n--){
string op;
int x;
cin>>op>>x;
if(op == "I") h[find(x)] = x;
else {
if(h[find(x)] == null) cout<<"No"<<'\n';
else cout<<"Yes"<<'\n';
}
}
return 0;
}
解释:开放寻址法其实就跟你去公厕找坑位一样,你用一个hash函数算出了从那个地方开始能遇到空坑位的机会最大,并从那个地方开始往后寻找,直到找到第一个空坑位为止,如果找到最后一个还没找到,就退回到0开始找。(因为这里不需要考虑重复性,所以如果x已经找到坑位了,我们就把x关在同一个屋子里);
###拉链法
#include <cstring>
#include <iostream>
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;
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 n;
scanf("%d", &n);
memset(h, -1, sizeof h);
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;
}
解释:其实总体来讲,这个拉链法和开放寻址法大同小异,就可以简单地理解为一个链上有很多条不同head的单链表,也可以参考vector的二维数组;
不难,但是开放寻址法会更优一些!