如何实现固定大小的hashmap?

时间:2014-06-13 11:05:02

标签: c hash hashmap

我想实现一个hashmap,但我不允许它扩展。因为我知道我需要存储大多数N个元素,所以我可以为哈希表的每个桶预先分配一个带有N元素的数组,这样我仍然可以存储N在最坏的情况下,所有密钥都在同一个桶上进行散列的元素。但是我需要存储的元素相当大,所以对于大N来说,这是非常低效的内存使用。

是否可以使用固定数量的内存实现具有效率(就内存而言)的散列图,例如:通过实现智能散列函数?

(PS:密钥是一个无符号的32位整数,除了我将收到的键值在该范围的一个相当小的子集中,我之前没有关于键的知识,并且这个子集移动得非常缓慢在范围内。)


我现在有一个实现,其中我有两个长度为N的数组,一个包含元素,另一个包含与两个数组中位置i的元素对应的键。我使用模运算作为哈希函数来确定元素应该插入/存在的位置,并使用线性探针来查找碰撞时最近的空白点。我认为这是复杂的O(N),我认为这对于我期望的数据量来说会相当快。我问了这个问题,看看能否做得更好。

1 个答案:

答案 0 :(得分:3)

对于散列,您可以使用以下代码段,Linux内核使用它来散列PID:

unsigned long hash_long(unsigned long val, unsigned int bits)
{
unsigned long hash = val * 0x9e370001UL;
return hash >> (32 - bits);
}

幻数0x9e370001UL是一个大素数。以下是了解Linux内核的摘录,解释了幻数:

  

您可能想知道0x9e370001常量(= 2,654,404,609)的来源   从。此哈希函数基于索引的乘法   一个合适的大数,使结果溢出和值   保留在32位变量中可以认为是a的结果   模数运算。 Knuth认为可以获得良好的结果   当大乘数是一个近似黄金比例的素数时   232(32位是80×86寄存器的大小)。现在,   2,654,404,609是靠近的一个素数,也可以很容易地成倍增加   通过加法和位移,因为它等于2 ^ 31 + 2 ^ 29 -   2 ^ 25 + 2 ^ 22 - 2 ^ 19 - 2 ^ 16 + 1。

右移hash >> (32 - bits);只是说哈希值中的位数。其他位将被清零。在您的情况下,将由限制N确定。为了使其按原样工作,N需要使其在其最重要的设置位之后的所有位都被设置,例如,对于N = 7(其中最后三位都已设置且所有其他位均为零)且将为3.或N = 63其中最低有效6位全部设置且所有其他位都为零。这里将为6。

hash_long函数返回的值将在您的数组中形成索引。

处理冲突

要处理冲突,只保留一个数组,但要使其成为链表节点数组。因此,数组中的每个元素都指向一个链表。发生冲突时,只需将新条目附加到与阵列中该插槽对应的链接列表的末尾。

处理冲突(更新)

如果你不能动态分配新的内存那么你发布的解决方案似乎没问题,虽然我不确定只包含键的数组的目的是什么(键不应该是它所属的元素的成员? )。以下是对您的解决方案的建议:

拥有1-D数组意味着在发生碰撞时,我们在插入和检索时都执行线性探测。另一种方法是使用二维数组,其中内部数组充当链表。我们需要在每个内部数组中插入最后一个元素的索引。与1-D数组相比较的缺点是,如果在同一个索引上发生太多碰撞,那么我们可能会在其中一个内部数组中耗尽空间,除非我们使每个内部数组长度为N,这将导致浪费了很多空间。优点是插入时,我们不需要执行线性探测。我们只检查内部数组中最后一个元素的索引并将其递增1以获得插入新元素的下一个插槽。