构造哈希表/哈希函数

Posted

技术标签:

【中文标题】构造哈希表/哈希函数【英文标题】:Constructing a hash table/hash function 【发布时间】:2011-02-27 01:54:24 【问题描述】:

我想构建一个哈希表,在 1 到 15 个字节的字节序列(字符串)中查找键。

我想存储一个整数值,所以我想一个用于散列的数组就足够了。我很难概念化如何构造一个散列函数,以便给定键会给数组一个索引。

任何帮助将不胜感激。

哈希中的最大条目数为:4081*15 + 4081*14 + ... 4081 = 4081((15*(16))/2) = 489720。

例如:

int table[489720];

int lookup(unsigned char *key)

    int index = hash(key);
    return table[index];

哈希函数有哪些不错的选择,或者我将如何构建一个?

谢谢。

【问题讨论】:

如果两个键映射到同一个索引,就会发生冲突,在您的示例中未正确处理。您只是为了说明您的散列而保留您的示例,还是您真的需要关于散列表本身的额外解释? (开放散列,封闭散列,...) 【参考方案1】:

如果您想要一个完美的散列,那么您可以从阅读***文章perfect hashing 开始。如果遇到问题,可以在这里寻求帮助。

【讨论】:

【参考方案2】:

如果驻留在表中的字符串的平均数量很少(例如低于 10,000 个条目),则关联数组将是一种合理的方法,即使在现代 CPU 架构上使用线性搜索也是如此。

否则,构建“完美哈希”需要检查字符串的每个字符并根据可能的范围计算唯一值。例如,如果键中只允许包含 26 个字符 A..Z,则可以这样做:

int
hash (const char *key)

   int h = 0;
   while (key && *key)
       h = h * 26 + (*key++ - 'A');
   return h;

【讨论】:

这将在 7 个字符后溢出 32 位 int,在 14 个字符后溢出 64 位 int。不是查找表的好索引...【参考方案3】:

您的密钥空间很大(大约 2^(8*15)),因此如果您想要一个完美的哈希,您需要提前知道 489720 个实际密钥会显示什么。即使这样,实际上也不可能为这些键找到完美的散列,即使您允许更大的表(也就是非常低的负载因子)。我知道找到完美哈希的唯一方法是反复试验,除非您的表有接近 489720^2 个条目,否则随机哈希可能会失败。

我强烈建议使用regular (non-perfect) hash 和deal with collisions appropriately,例如使用链接:

struct entry 
  unsigned char *key;
  int value;
  struct entry *next;
 *table[1<<20];
int lookup(unsigned char *key) 
  int index = hash(key) % (1<<20);
  for (struct entry *e = table[index]; e != NULL; e = e->next) 
    if (!strcmp(key, e->key)) return e->value;
  
  // not found

我还建议您不要自己实现此功能 - 使用像 c++ hashmap 这样的标准库。

【讨论】:

【参考方案4】:

为了散列 C 字符串,我一直使用这个函数(取结果 % 你的散列表的大小):

int hashstring(const char* s) 
  int key = 0;
  while (*s) 
    key = key*37 + *s++;
  
  return key;

我不记得我最初是从哪里得到它的,但多年来它并没有让我失望。

【讨论】:

对不起,但无法得到。这里的 37 和问题中的 4081 有什么意义。

以上是关于构造哈希表/哈希函数的主要内容,如果未能解决你的问题,请参考以下文章

数据结构哈希表

哈希函数的常用构造方法

iOS中的哈希表

哈希算法和哈希表的区别?

C++进阶第二十一篇——哈希(概念+哈希函数+哈希冲突+哈希表+哈希桶+代码实现)

哈希表与哈希(Hash)算法