我试图了解开放寻址方法。我参考T. H. Cormen关于这个主题的书,其中指出在开放式寻址中删除是困难的。我完全坚持这一段:
从开放地址哈希表中删除很困难。当我们从广告位
i
中删除密钥时,我们不能通过在其中存储NIL
来将该广告位标记为空。这样做可能会导致无法检索任何键k
,在其插入过程中我们已探测到插槽i
并发现它已被占用。
我不明白这一点。请用一些例子来解释。
答案 0 :(得分:44)
假设hash(x) = hash(y) = hash(z) = i
。并假设首先插入x
,然后y
然后z
。
在开放式地址中:table[i] = x
,table[i+1] = y
,table[i+2] = z
。
现在,假设您要删除x
,并将其重新设置为NULL
。
稍后您将搜索z
,您会发现hash(z) = i
和table[i] = NULL
,并且您将返回错误答案:z
不在表格中。
要解决此问题,您需要使用特殊标记设置table[i]
,以指示搜索功能继续查看索引i+1
,因为可能存在其哈希值为{{1 }}
答案 1 :(得分:11)
在开放寻址方案中,查找会调用一系列探测,直到找到密钥或找到空插槽。
如果一个钥匙涉及多个探头的链条,那么如果沿着链条的某个地方,其他钥匙中的一个被移除,则会丢失(找不到),留下需要踏脚石的空槽。
通常的解决方案是通过将其插槽标记为可用于重用但实际上不为空来删除密钥。换句话说,添加了替换垫脚石,以便不会缩短与其他键的探针链。
希望这有助于您理解。
答案 2 :(得分:7)
从线性探测开放寻址哈希表中删除很简单。维基百科哈希表页面上有伪代码多年。我不知道为什么还没有,但是这里有一个永久链接:Old Wikipedia Hash Table page,为了您的方便,这里是伪代码:
function remove(key)
i := find_slot(key)
if slot[i] is unoccupied
return // key is not in the table
j := i
loop
j := (j+1) modulo num_slots
if slot[j] is unoccupied
exit loop
k := hash(slot[j].key) modulo num_slots
if (j > i and (k <= i or k > j)) or
(j < i and (k <= i and k > j)) (note 2)
slot[i] := slot[j]
i := j
mark slot[i] as unoccupied
该页面上还有一些real code的参考号。我相信这与插入具有完全相同的性能特征。
这种删除方法比删除的常用标记更好,并且偶尔会重新删除所有内容&#39;因为上述方法是恒定时间而不是摊销的常数时间。如果您要添加和删除的一百万个项目的哈希表,则删除&#39;标记已删除&#39;方法,偶尔添加或删除比它之前和之后的时间长一百万倍 - 这不是一个好的性能特征。