使用线性探测时处理哈希冲突

时间:2011-11-19 15:02:24

标签: java hashtable

我已经阅读了关于哈希表和开放式adrdessing的内容。如果要在大小为13的哈希表中插入密钥:18,32,44:

18 gets index 5 (18 modulus 13 = 5)
32 gets index 6 (32 modulus 13 = 6)
44 gets index 5 (44 modulus 13 = 5)

你会发生碰撞,因为索引5上已有东西。

如果您使用线性探测,您将hashfunction = (key+i) modulus N i = 0,1,2.. hashfunction(44)=5,直到您在哈希表中找到空位。然后将在索引7处插入44。

如果你删除了32,然后你想要删除,那么44。首先查看hashfunction(44 + 1) = 6 - 不是44,然后是{{1}} - 这是空的。然后你可能会认为44已经消失了。你如何在哈希表中标记一个地方,该地方不是真的空,但不包含密钥,你应该继续在下一个索引处寻找44?

如果您需要在索引6处插入另一个键,则该键只会覆盖哈希表中的“标记”。

您可以使用什么来标记索引 - 这里说的是关键,但已被删除 - 所以你继续看下一个索引?你不能只写null或0,因为你认为密钥已被删除(null)或者值为0的密钥已被覆盖44。

4 个答案:

答案 0 :(得分:5)

使用开放寻址处理哈希表的一种方法是使用状态标记:EMPTYOCCUPIEDDELETED。请注意,EMPTY之间存在重要区别,这意味着该位置从未使用过DELETED,这意味着它已被使用但已被删除。

删除值后,广告位将标记为DELETED,而不是EMPTY。当您尝试检索某个值时,您将进行探测,直到找到标记为EMPTY的插槽为止;例如:您认为DELETED个广告位与OCCUPIED相同。请注意,插入可以忽略这种区别 - 您可以插入DELETEDEMPTY广告位。

问题是标记Java,这有点误导,因为Java(或至少Oracle的实现)不使用开放寻址。当负载因子变高时,开放寻址会产生特殊问题,这会导致更频繁地发生哈希冲突:

enter image description here

正如您所看到的,在0.7附近有一个戏剧性的性能下降。大多数哈希表在其加载因子超过某个常数因子后会调整大小。例如,当加载因子超过0.75时,Java将其HashMap的大小加倍。

答案 1 :(得分:2)

您似乎正在尝试实现自己的哈希表(与使用java中包含的Hashtable或HashMap相比),因此它更像是一个数据结构问题,而不是java问题。

话虽这么说,在删除元素时,实现具有开放寻址的哈希表(例如线性探测)并不是非常有效。通常的解决方案是“拉出”错误插槽中的所有元素,这样探测中就不会有任何空格。

在维基百科上有一些伪代码很好地描述了这个:

http://en.wikipedia.org/wiki/Open_addressing

答案 2 :(得分:0)

哈希表桶不限于存储单个值。因此,如果两个对象散列到表中的相同位置,则它们都将被存储。碰撞只意味着查找会稍微慢一点,因为当使用散列到特定位置的键查找值时,它需要检查每个条目以查看它是否匹配

听起来你正在描述一个哈希表,你只存储一个条目和每个索引。我能想到的唯一方法是在结构中添加一个字段,存储指示该位置是否发生碰撞的值。然后在进行查找时,您需要检查密钥,如果匹配则具有该值。如果没有,那么你会检查是否有碰撞,然后检查下一个位置。在移除时,您必须保留碰撞标记但删除值和键。

答案 3 :(得分:0)

如果你使用一个使用这种方法的哈希表(没有内置的哈希集合),你需要遍历所有后面的键,看看它们是否需要向上移动(以避免漏洞)。有些可能是相同的哈希值,有些可能是无关哈希码的冲突。如果你这样做,你就不会留下任何漏洞。对于不太满的哈希映射,这不应该产生太多开销。