Hash表由hash函数(映射函数)与邻接表共同实现,常用于将庞大的信息映射到一个更小的值域内进行维护。(如统计序列中不同数字出现的次数)
当hash函数设计较好时,原始信息会被较均匀地分配到各个表头之后,从而使每次查找的时间复杂度近似为O(1)。
最常用的hash函数:hash(x) = (x mod P) + 1(P是一个较大的质数),这样能使x从原来的值域变为1~P。
例题:POJ3349 Snowflake Snow Snowflakes(有最多105片雪花,每片雪花由六个角的数值(0~107-1)组成,当两个雪花从任一个角开始顺时针/逆时针往后能得到相同的六元组时,这两个雪花相同。现给定所有雪花的信息(某一角开始顺/逆时针得到的六元组),判断所有雪花中有无相同的雪花)。
解题思路:
设ai表示第i个角的长度,设计hash函数为(还有更好的hash函数,可自行设计)
h
a
s
h
(
x
)
=
(
Σ
i
=
1
6
a
i
2
)
m
o
d
(
1
0
5
+
1
)
hash(x) = (\overset {6}{\underset {i=1}{\Sigma}} a_i^2)mod (10^5+1)
hash(x)=(i=1Σ6ai2)mod(105+1)对于两片相同的雪花,他们的hash函数值必然相等。因此本题借助Hash表完成对两片雪花是否相同的判断,期望时间复杂度为O(n)。
题解代码如下
// POJ3349
# include <cstdio>
# include <algorithm>
using namespace std;
const int MOD = 1e5+1;
const int MAXN = 1e5;
int head[MOD], tot;
struct { int a[6], next;} ha[MAXN+1];
bool same(int a[], int b[]); // 判断两片雪花是否相同
int main() {
int n, b[6], t, p, pre_p; scanf("%d", &n); tot = 0;
while (n --) {
t = 0;
for (int i = 0; i < 6; i ++) {
scanf("%d", &b[i]); t = (t + (long long)b[i]*b[i]) % MOD;
}
pre_p = 0, p = head[t];
while(p) { // hash值相同,进行枚举判断
if (same(ha[p].a, b)) {
printf("Twin snowflakes found.\n"); return 0;
}
pre_p = p; p = ha[p].next;
}
if (!pre_p) head[t] = ++tot; else ha[pre_p].next = ++tot;
for (int i = 0; i < 6; i ++) ha[tot].a[i] = b[i];
}
printf("No two snowflakes are alike.\n");
return 0;
}
bool same(int a[], int b[]) { // 算法示例
int pa, pb, pb2, p0 = -1;
for (int i = 0; i < 6; i ++) {
if (a[i] != a[0]) continue; pa = i, pb = 0;
if (!(p0+1)) {
while (pb < 6 && a[0] != b[pb]) pb += 1;
if (pb >= 6) return false; p0 = pb;
}
pa = (pa+1)%6, pb = p0; pb2 = pb; bool f1 = true, f2 = true;
while (pa != i && (f1 || f2)) {
pb = (pb+1)%6; pb2 = (pb2+5)%6;
if (a[pa] != b[pb]) f1 = false; if(a[pa] != b[pb2]) f2 = false;
pa = (pa+1)%6;
}
if (f1 || f2) return true;
}
return false;
}