【发布时间】:2011-01-21 20:02:42
【问题描述】:
我当前的哈希表实现是使用线性探测,现在我想转向二次探测(然后是链接,也许还有双重哈希)。我已经阅读了一些文章、教程、维基百科等……但我仍然不知道我应该做什么。
基本上,线性探测的步长为 1,这很容易做到。在哈希表中搜索、插入或删除元素时,我需要计算一个哈希值,为此我这样做:
index = hash_function(key) % table_size;
然后,在搜索、插入或删除时,我循环遍历表,直到找到一个空闲存储桶,如下所示:
do {
if(/* CHECK IF IT'S THE ELEMENT WE WANT */) {
// FOUND ELEMENT
return;
} else {
index = (index + 1) % table_size;
}
while(/* LOOP UNTIL IT'S NECESSARY */);
至于二次探测,我认为我需要做的是改变“索引”步长的计算方式,但我不明白我应该怎么做。我见过各种各样的代码,它们都有些不同。
此外,我还看到了一些二次探测的实现,其中更改了哈希函数以适应这种情况(但不是全部)。是否真的需要进行这种更改,或者我可以避免修改散列函数并仍然使用二次探测吗?
编辑: 在阅读了下面 Eli Bendersky 指出的所有内容后,我想我明白了。这是http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_hashtable.aspx的部分代码:
15 for ( step = 1; table->table[h] != EMPTY; step++ ) {
16 if ( compare ( key, table->table[h] ) == 0 )
17 return 1;
18
19 /* Move forward by quadratically, wrap if necessary */
20 h = ( h + ( step * step - step ) / 2 ) % table->size;
21 }
有两件事我不明白......他们说二次探测通常使用c(i)=i^2 完成。然而,在上面的代码中,它更像是c(i)=(i^2-i)/2
我已经准备好在我的代码上实现这一点,但我只会这样做:
index = (index + (index^index)) % table_size;
...而不是:
index = (index + (index^index - index)/2) % table_size;
如果有的话,我会这样做:
index = (index + (index^index)/2) % table_size;
...因为我看到其他代码示例跳水了两个。虽然不明白为什么……
1) 为什么要减去步长?
2) 为什么要减去 2?
【问题讨论】:
-
请记住,二次探测仅在表大小为素数且负载因子 eternallyconfuzzled.com/tuts/datastructures/… 了解不同冲突解决策略的概述
-
@Cristoph:这种说法不太正确。如果表大小是素数,那么如果负载因子 唯一情况。例如,它对 2 的幂的表大小和任意负载因子也有效(请参阅我的答案)。
-
@Mathew:“工作”和“有效工作”是有区别的;如果负载因子太高,(二级)集群可能会再次成为问题
-
@Cristoph:当然(“任意负载因子”对我来说可能是一个糟糕的选择;无论花哨的探测如何,0.999 的负载因子都不是一个好主意)。但是 0.75 是一个合理的负载因子,即使使用线性探测(只要散列函数良好),哈希表也能很好地工作,并且确实可以有效地使用二次探测和 2 的幂表大小(探测访问所有存储桶,如线性探测,但减少了主聚类)-因此“二次探测仅在表大小为素数且负载因子小于 0.5 时才有效”的语句不正确。
标签: c hashtable hash-collision quadratic-probing