一、 定义
- 哈希表是又称散列表,一种以 “key-value” 形式存储数据的数据结构(
h[key] = value
)。输入查找的值 key,就可以快速地找到其对应的 value。- 可以把哈希表理解为一种高级的数组,这种数组的下标可以是很大的整数,浮点数,字符串甚至结构体。
但如果key值很大超出下标范围,这时就体现了哈希的作用,也就是key值的使用。
二、key值的使用
- 当 key 的范围比较小的时候,可以直接把 key 作为数组的下标。
- 当 key 的范围比较大,比如以
10^9
范围内的整数作为 key 的时候,就需要用到哈希表。一般把 key 模一个较大的质数(M)作为索引(key = key % M
)。- 当 key 为字符串的情况,一般不直接把字符串作为 key,而是先算出字符串的哈希值,再把其哈希值作为 key 插入到哈希表里。
如果两个不同的值模完后的值是相同的这时就产生了一个关键性的现象,冲突。
解决冲突最长用的两种方法:开放寻址法,拉链法。
开放寻址法
思路
value
值摸完以后的key
值(key = value % M
)作为数组h
下标,如果此时数组h[key]
不为空,我们就让key
向后移一位,直至数组h[key]
为空时,将value
加入。
代码实现:
int find(int v)
{
int k = (v % N + N) % N; // 加N的目的是为了让负数v变为正数
while (h[k] != INF && h[k] != v) //INF为大于v范围的数
{ //哈希表例没有重复的value,每一个value都有唯一的key与之对应
k++; // 不为空,不重复,往后移
if (k == N) k = 0; //到尾在从头找
}
return k; // 不存在v返回INF的位置(为了value加入 h[k] = value)
// 存在返回value存的位置,通过h[k]可以找到value
}
拉链法
思路
拉链法是在每个存放数据的地方开一个链表,如果有多个
value
对应的key
(key = value % N
) 索引到同一个地方,只用把他们都放到那个位置的链表里就行了。查询的时候需要把对应位置的链表整个扫一遍。
数组链表实现:
void insert(int v)
{
int k = (v % N + N) % N;
e[idx] = v; // idx结点
ne[idx] = h[k]; // 下一个结点
h[k] = idx++; // 更新链
}
int find(int v)
{
int k = (v % N + N) % N;
for (int i = h[k]; i != -1; i = ne[i]) // h[k]初始值为-1,-1为空
if (e[i] == v) return true;
return false;
}
以上的N均取大于输入数据范围的最小质数。
字符串哈希
字符串匹配
求出模式串的哈希值后,求出文本串每个长度为模式串长度的子串的哈希值,分别与模式串的哈希值比较即可。
例:文本串aabbaa
对应的存放方法
h[1] = a
,h[2] = aa
,h[3] = aab
,h[4] = aabb
,h[5] = aabba
,h[6] = aabbaa
。
实际哈希数组h不是字符类型,所以要将字符转换成数字这里称哈希值。
哈希值求法
- 哈希值是一个p进制数,公式为
h[i] = (h[i-1] * p + 对应最后一个字符的ASCII码值) % Q
。
例:h[1] = 0 * p + 97(a的ascll)
,h[2] = h[1] * p + 97
······(不管Q后面会讲)- 哈希值不一样时要保证两个字符串大概率不一样(一般可以将大概率看成一定)。
当p值取131 或 13331
,Q
值取264 就表示近似避免冲突。- Q 值在计算中不必写出来,只要将
h
数组类型写成unsigned long long
就可以代替。
区间哈希值求法
- 例:
12345
十进制数要求第四位(l = 4
)到第五位 (r = 5
)的值。
很显然值为45
,方法:12345 - 123 * 100 = 45
。- 同理 要求字符
aabbaa
第四位(l = 4
)到第五位 (r = 5
)的哈希值。
方法:hash = h[5] - h[3] * p ^ (5-3)
。总结区间哈希值公式为:
h[l,r] = h[r] − h[l−1] × p^(r−l+1)
。
代码实现
typedef unsigned long long ull;
const int p = 131;
ull q[N],h[N]; //q数组为p的n次方
ull get(int l, int r)
{
return h[r] - h[l - 1] * q[r - l + 1]; //区间哈希值求法公式
}