哈希表(二)——哈希函数(字符串哈希算法)

在上一篇文章哈希表的大小提到过一种除留余数法的计算哈希值的函数。这一篇文章来具体说一说,怎么设计哈希函数能够让哈希表更加效率。

哈希函数,是用来计算存储数据的哈希值的,根据存储数据的类型,可以设计不同的哈希函数。一个好的哈希函数(让哈希表效率高的函数),一般都具备下面两个特点:

  1. 速度快(别计算一个哈希值计算了半天,导致效率很低,简单高效就好)
  2. 能够将得到的哈希值均匀地分布在整个哈希表中,保证不产生聚集(不要让一堆数据都聚集在哈希表的一部分,这样之后的数据插入进来就很容易产生哈希冲突)

通常一个哈希函数具有下面的形式

哈希值 = 计算后的存储值 / 哈希表的大小

对于如果存储的数是整数这种类型,我们完全可以不用计算,直接将整数的值作为上式中计算后的存储值。

而对于字符串这种类型,当然不仅仅是字符串,我们都要设计一个相对较好的算法,来计算出它们的存储值。

所以,我们以字符串为例,来介绍一下常见的字符串哈希算法,其他类型的数据都可以用相似的思路来设计适合自己的哈希算法。

字符串哈希算法

马上就能想到的算法:简单地将字符串中每个字符的ASCII码加起来

size_t stringHash(const string& key){
    size_t hashKey = 0;

    for(size_t i = 0; i < key.size(); ++i)
        hashKey += key[i];
    
    return hashKey;
}

用上面的方法可以很快地算出哈希值,但是如果表很大时,则函数就不能很好的分配。比如我的表的大小是10000,即我的数据规模大概是7000个左右(取装填因子为0.7),但是我的字符最多只有8个字符长,由于ASCII码最大值是127,因此hash函数计算出来的哈希值只能在0-1016之间取值,其中127 * 8 =1016,这就会有一种聚集的效果,这就不是我们上面提到的两点想要的,我们要尽可能地避免聚集。

这个方法可能是刚接触字符串哈希函数的人会马上想到的,但其实我们有很多的优秀的字符串哈希算法。

优秀的字符串哈希算法

BKDR哈希算法

size_t BKDR_hash(const string& key){
    size_t hashKey = 0;
    size_t seed = 131;    //也可以是31 131 1313 13131 131313
    for(size_t i = 0; i < key.size(); ++i)
        hashKey += hashKey * seed + key[i];

    return hashKey;
}

根据上面的算法,我们就可以根据结果得到非聚合的一些哈希值。

这个算法是效率很高的一个算法,其他的字符串算法可以看这里:字符串哈希算法,是人家总结的一篇文章,涵盖了当今很多的哈希算法。

猜你喜欢

转载自blog.csdn.net/lvyibin890/article/details/82220204