[译]C语言实现一个简易的Hash table(3)

上一章,咱们讲了hash表的数据结构,并简单实现了hash表的初始化与删除操做,这一章咱们会讲解Hash函数和实现算法,并手动实现一个Hash函数。算法

Hash函数

本教程中咱们实现的Hash函数将会实现以下操做:安全

  • 输入一个字符串,而后返回一个0m(Hash表的大小)的数字
  • 为一组日常的输入返回均匀的bucket索引。若是Hash函数不是均匀分布的,就会将多个记录插入到相同的bucket中,这就回提升冲突的概率,而这个冲突就会影响到咱们的Hash表的效率。

Hash算法

咱们将会设计一个普通的字符串Hash函数,在伪代码中表示以下:数据结构

function hash(string, a, num_buckets):
    hash = 0
    string_len = length(string)
    for i = 0, 1, ..., string_len:
        hash += (a ** (string_len - (i+1))) * char_code(string[I])
    hash = hash % num_buckets
    return hash

这个Hash函数主要分为两步:函数

  1. 将字符串转为大整型
  2. 经过取余数mod m将整数的大小减少到固定范围

变量a是一个素数,而且要大于英文字母,咱们正在散列ASCII字符串,其字母大小为128,所以咱们应该选择大于此的素数。测试

char_code这个函数会返回字母对应的整数,使用的是ASCII中的字母。.net

以下使用这个Hash函数设计

hash("cat", 151, 53)

// 函数拆解
hash = (151**2 * 99 + 151**1 * 97 + 151**0 * 116) % 53
hash = (2257299 + 14647 + 116) % 53
hash = (2272062) % 53
hash = 5

若是改变a咱们会获得不一样的结果:code

hash("cat", 163, 53) = 3

代码实现

// hash_table.c
static int ht_hash(const char* s, const int a, const int m) {
    long hash = 0;
    const int len_s = strlen(s);
    for (int i = 0; i < len_s; i++) {
        hash += (long)pow(a, len_s - (i+1)) * s[i];
        hash = hash % m;
    }
    return (int)hash;
}

什么是冲突?

理想中的散列函数返回的结果都是均匀分布的,可是,对于任意一个散列函数,总会有一些输入通过散列后,获得相同的值。若是要找到这组输入,咱们就须要测试大量的输入数据。blog

由于上面提到的有很差的输入存在,意味着全部输入都没有完美的散列函数。因此在设计散列函数时,针对预期输入,咱们的散列函数须要表现最好。教程

很差的输入也存在安全问题,若是某个恶意用户向哈希表提供了一组冲突密钥,那么搜索这些密钥将比正常状况(O(1))花费更长时间(O(n))。这能够用做针对以哈希表为基础的系统(例如DNS和某些Web服务)的拒绝服务攻击。

上一章:Hash table数据结构 下一章:冲突处理

相关文章
相关标签/搜索