前言:

在复习hash表的时候遇到了困难,这玩意儿实在是太抽象了。

我感觉这种与数学沾染了的算法与数据结构,都会变得很抽象。

这里讲的是自己手写hash表,而不是stl库中的hash表。

主要是因为有时候引用了stl的undered_map可能会超时,所以下面来模拟一下映射与寻址。

概述:

手写hash表有两种方法,这里讲的是开放寻址法。

开放寻址法

先定义一个hash数组

1
int h[N];//N为数据范围的2倍以上,越大映射得越好。

接下来便是开放寻址

1
2
3
4
5
6
7
8
int find(int x){
int t=x(x%M+M)%M;//映射到hash数组上
while(h[t]!=-1&&h[t]!=x){//寻址的过程
if(++t==m)
t=0;
}
return t;
}

实践:

接下来我们直接上一道蓝桥杯的难题以供大家理解hash表

题目:

在一个二维平面上放置着 n个炸雷,第 i个炸雷 (xi,yi,ri)表示在坐标 (xi,yi)处存在一个炸雷,它的爆炸范围是以半径为 ri的一个圆。

为了顺利通过这片土地,需要玩家进行排雷。

玩家可以发射 m个排雷火箭,小明已经规划好了每个排雷火箭的发射方向,第 j个排雷火箭 (xj,yj,rj)表示这个排雷火箭将会在 (xj,yj)处爆炸,它的爆炸范围是以半径为 rj的一个圆,在其爆炸范围内的炸雷会被引爆。

同时,当炸雷被引爆时,在其爆炸范围内的炸雷也会被引爆。现在小明想知道他这次共引爆了几颗炸雷?

你可以把炸雷和排雷火箭都视为平面上的一个点。一个点处可以存在多个炸雷和排雷火箭。当炸雷位于爆炸范围的边界上时也会被引爆。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e4+5,M=2e5+7,X=1e9+1;
class circle{
public:
int x,y,r;
}c[N];
int id[M];
LL h[M];
bool st[M];
int n,m;
LL get_value(int x,int y){//将坐标映射成一维数据
return x*X+y;
}
LL find(int x,int y){
int value=get_value(x,y);
int t=(value%M+M)%M;//找到相应hash数组下标
while(h[t]!=-1&&h[t]!=value){//如果该下标有数据同时hash数组的val值不等于value往后找
if(++t=M)//当从t搜索到末尾还没有,从头开始搜索
t=0;
}
return t;
}
int sqr(int x){
return x*x;
}
void dfs(int x,int y,int r){
st[find(x,y)]=true;//该点的炸雷已经引爆过
for(int xx=x-r;xx<=x+r;xx++){
for(int yy=y-r;yy<=y+r;yy++){
if(sqr(yy-y)+sqr(xx-x)<=sqr(r)){
int t=find(xx,yy);
if(!st[t]&&id[t]){
dfs(xx,yy,c[id[t]].r);
}
}
}
}
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);//初始化hash数组
for(int i=1;i<=n;i++){
int x,y,r;
cin>>x>>y>>r;
c[i]={x,y,r};
int t=find(x,y);
while(h[t]==-1)h[t]=get_value(x,y);
if(!id[t]||c[id[t]].r<r){//该点没有过炸雷,或者之前的炸雷半径比现在的小
id[t]=i;//该坐标有炸雷
}
}
while(m--){
int x,y,r;
cin>>x>>y>>r;
for(int xx=x-r;xx<=x+r;xx++){
for(int yy=y-r;yy<=y+r;yy++){
if(sqr(xx-x)+sqr(yy-y)<=sqr(r)){//判断是否在圆内
int t=find(xx,yy);
if(!st[t]&&id[t]){//该点没有被dfs过,同时该点有炸雷
dfs(xx,yy,c[id[t]].r);
}
}
}
}
}
int res=0;
for(int i=1;i<=n;i++){//遍历每个炸雷,统计被引爆的炸雷
if(st[find(c[i].x,c[i].y)]){//若炸雷被引爆,res++;
res++;
}
}
cout<<res;
return 0;
}