Hash表
一般只有添加、查找
(注意:离散化为特殊的哈希方式,因为离散化需要提前保序)
作用
将一堆数据 通过hash函数映射为 0 ~ N的数
hash函数:x mod N(N一般取质数)
当不同的数映射成一个数时,产生冲突,处理冲突有两种方法。
① 拉链法
(存放下标的数组h初始化为-1)
插入
void insert(int x)
{
int k = (x % N + N) % N;
e[idx] = x;
ne[idx] = h[k];
h[k] = idx ++;
}
注意 :求其余数时,值可正可负(-10 % 3 == -1),需要保证为正数
%N + N ) % N
,不可 + N ) % N
,x在-1e+09 ~ 1e+09,N为 10^5,这样无法保证k为正数
查找
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;
}
② 开放寻址法
当映射时,若该位已有数,则继续向后查找,直至找到空位,存下。
(数组存放的是数值,初始化为无穷大即可,规定一个值 null)
//若x存在返回其位置,若x不存在返回其应该在的位置。
int find(int x)
{
int k = (x % N + N) % N;
while(h[k] != null && h[k] != x)
{
k++;
if(k = N) k = 0; //k到最后一个,则从头循环
}
return k;
}
-
当
h[k] == x
时:说明已存在x,返回其位置即可。 -
当
h[k] == null
时:说明不存在x,返回其应该在的位置。注意后续需要将x填入。
例 - 模拟散列表
> 拉链法
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200003;
int h[N];
void insert(int x)
{
int k = (x % N + N) % 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])
{
if(e[i] == x) return true;
}
return false;
}
int main()
{
memset(h,-1,sizeof h );
int n,x;
scanf("%d",&n);
char op[2];
while(n--)
{
scanf("%s",op);
if(op[0] == 'I')
{
scanf("%d",&x);
insert(x);
}
else
{
scanf("%d",&x);
if(!find(x)) puts("No");
else puts("Yes");
}
}
return 0;
}
> 开放寻址法
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200003;
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()
{
memset(h,0x3f,sizeof h );
int n,x;
scanf("%d",&n);
char op[2];
while(n--)
{
scanf("%s",op);
if(op[0] == 'I')
{
scanf("%d",&x);
h[find(x)] = x;
}
else
{
scanf("%d",&x);
if(h[find(x)] == null) puts("No");
else puts("Yes");
}
}
return 0;
}
注意:
以上两种方法h[k]
代表的含义不同
拉链法:映射到k的数的下标 idx
开放寻址法:映射到k的数(k可能为负数,数组初始化时)