对于散列,您可以使用以下 sn-p,顺便说一句,Linux 内核使用它来散列 PID:
unsigned long hash_long(unsigned long val, unsigned int bits)
{
unsigned long hash = val * 0x9e370001UL;
return hash >> (32 - bits);
}
幻数0x9e370001UL 是一个大素数。以下是 Understanding Linux Kernel 的摘录,解释了这个神奇的数字:
您可能想知道 0x9e370001 常量 (= 2,654,404,609) 的来源
从。此哈希函数基于索引乘以
一个合适的大数,使结果溢出,值
剩余在 32 位变量中可以被认为是一个结果
模运算。 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); 只是说在哈希值中保留bits 位数。其他位将被清零。在您的情况下,bits 将由限制 N 确定。要使其按原样工作,N 需要使其最高有效设置位之后的所有位也被设置,例如对于N = 7(其中最后三位均已设置,所有其他位为零),bits 将为 3。或N = 63,其中最低有效六位均已设置,所有其他位均已设置零。这里 bits 将是 6。
hash_long 函数返回的值将形成数组的索引。
处理碰撞
要处理冲突,只保留一个数组,但将其设为链表节点数组。所以数组中的每个元素都指向一个链表。当发生冲突时,只需将新条目附加到与数组中该插槽对应的链表末尾即可。
处理碰撞(更新)
如果您不能动态分配新内存,那么您发布的解决方案似乎很好,尽管我不确定仅包含键的数组的目的是什么(键不应该是它所属元素的成员吗? )。以下是对您的解决方案的建议:
拥有一维数组意味着在发生冲突的情况下,我们在插入和检索时都执行线性探测。另一种方法是使用二维数组,其中内部数组充当链表。我们需要索引插入到每个内部数组中的最后一个元素。与一维数组相比,不利的一面是,如果在同一个索引上发生太多冲突,那么我们可能会用完一个内部数组中的空间,除非我们也将每个内部数组的长度设为 N,这将导致大量浪费空间。优点是在插入时,我们不需要执行线性探针。我们只需检查内部数组中最后一个元素的索引并将其加一以获取下一个插入新元素的插槽。