什么是hash
Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
什么是冲突
对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称碰撞。具有相同函数值的关键字对该散列函数来说称做同义词。这意味着哈希值是一个有限集合,而输入数据则可以是无穷多个,那么建立一对一关系明显是不现实的。所以“碰撞”是必然会发生的,所以一个成熟的哈希算法会有较好的抗冲突性,同时在实现哈希表的结构时也要考虑到哈希冲突的问题。
一般有通过将数据key进行四则运算的hash,还有位运算,查表法,混合法。较常使用的为取模法
取模:除余法就是用关键码x除以M(往往取散列表长度),并取余数作为散列地址。除余法几乎是最简单的散列方法,散列函数为: h(x) = (x % M + M) % M。,
链式地址法
上图为h(x) = (x % 13 + 13) % 13;
出现冲突时,只需要在当前位置下以单链表的形式连接就可继续储存数据。在查询数据时只需访问完全该单链表即可。
typedef unsigned long long ll;
#define M 13
//初始化
memset(h, -1, sizeof h);
ll h[13],ne[13],e[13],idx;
//在哈希表中插入一个数
void insert(int key)
{
int k = (key % M+ M) % M;
e[idx] = key;
ne[idx] = h[k];
h[k] = idx++;
}
// 在哈希表中查询某个数是否存在
int find(int key)
{
int k = (key % M + M) % M;
for (int i = h[k]; i != -1; i = ne[i])
if (e[i] == key)
return 1;
return 0;
}
线性探测再散列(开放寻址法)
对于任一产生冲突的key,将key经散列后,往后寻找可以存放数据的位置。而散列表长度一般设置为所需大小的两倍~三倍。
typedef unsigned long long ll;
#define M 13
//初始化
memset(h, 0x3f, sizeof h);
ll h[39],ne[39],e[39],idx;
//在哈希表中插入一个数
void insert(int key)
{
int t = (key % M + M) % M;
while (h[t] != 0x3f3f3f3f && h[t] != key)//查询可以存放key的位置
{
t++;
if (t == M) t = 0;
}
h[t] = key;
}
int find(ll key) {
int t = (key % M + M) % M;
while (h[t] != 0x3f3f3f3f && h[t] != key)
{
t++;
if (t == M) t = 0;
}
if (h[t] == 0x3f3f3f3f)return 0;//查询到的位置为空,即不存在该key
return 1;
}
字符串hash
多次询问子串,为一般采取的方法是对整个字符串先预处理出每个前缀的哈希值,将哈希值看成一个P进制的数对P取模的结果,这样的话每次就能快速求出子串的哈希了
将字符串看作为一个P进制的数
eg:原串s=aabaabab
s=s1s2s3s4s5.....
s[i]代表为原串第i个位置的前缀字串
h[0]=0;
h[1]=s1*P^0;
h[2]=s2*P^1;
h[3]=s3*P^2;
h[4]=s4*P^3;
h[5]=s5*P^4;
......
求L~R之间的字串为key=h[L]-h[R]*P^(R-L+1);
typedef unsigned long long ll;
//小技巧将p的次方保存下来
//可以通过 unsigned long long的数据溢出代替取模运算
//P进制的选取一般为 131 或13331 这两个数产生的冲突概率低
ll h[N],p[N];
char str[N];
//初始化
p[0] = 1;
for (int i = 1; i <= n; i++)
{
h[i] = h[i - 1] * P + str[i];
p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ll get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}