我已经阅读了关于哈希表和开放式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。
答案 0 :(得分:5)
使用开放寻址处理哈希表的一种方法是使用状态标记:EMPTY
,OCCUPIED
和DELETED
。请注意,EMPTY
之间存在重要区别,这意味着该位置从未使用过DELETED
,这意味着它已被使用但已被删除。
删除值后,广告位将标记为DELETED
,而不是EMPTY
。当您尝试检索某个值时,您将进行探测,直到找到标记为EMPTY
的插槽为止;例如:您认为DELETED
个广告位与OCCUPIED
相同。请注意,插入可以忽略这种区别 - 您可以插入DELETED
或EMPTY
广告位。
问题是标记Java,这有点误导,因为Java(或至少Oracle的实现)不使用开放寻址。当负载因子变高时,开放寻址会产生特殊问题,这会导致更频繁地发生哈希冲突:
正如您所看到的,在0.7附近有一个戏剧性的性能下降。大多数哈希表在其加载因子超过某个常数因子后会调整大小。例如,当加载因子超过0.75时,Java将其HashMap
的大小加倍。
答案 1 :(得分:2)
您似乎正在尝试实现自己的哈希表(与使用java中包含的Hashtable或HashMap相比),因此它更像是一个数据结构问题,而不是java问题。
话虽这么说,在删除元素时,实现具有开放寻址的哈希表(例如线性探测)并不是非常有效。通常的解决方案是“拉出”错误插槽中的所有元素,这样探测中就不会有任何空格。
在维基百科上有一些伪代码很好地描述了这个:
答案 2 :(得分:0)
哈希表桶不限于存储单个值。因此,如果两个对象散列到表中的相同位置,则它们都将被存储。碰撞只意味着查找会稍微慢一点,因为当使用散列到特定位置的键查找值时,它需要检查每个条目以查看它是否匹配
听起来你正在描述一个哈希表,你只存储一个条目和每个索引。我能想到的唯一方法是在结构中添加一个字段,存储指示该位置是否发生碰撞的值。然后在进行查找时,您需要检查密钥,如果匹配则具有该值。如果没有,那么你会检查是否有碰撞,然后检查下一个位置。在移除时,您必须保留碰撞标记但删除值和键。
答案 3 :(得分:0)
如果你使用一个使用这种方法的哈希表(没有内置的哈希集合),你需要遍历所有后面的键,看看它们是否需要向上移动(以避免漏洞)。有些可能是相同的哈希值,有些可能是无关哈希码的冲突。如果你这样做,你就不会留下任何漏洞。对于不太满的哈希映射,这不应该产生太多开销。