在我的实现中,我使用延迟删除与线性或二次探测进行冲突解决。对于插入,当我遇到一个延迟删除的项目时,我将其替换为要插入的项目。这样做有什么缺点或不正确(对于线性或二次或双重哈希冲突解决方案)?它不节省空间吗?
答案 0 :(得分:1)
开放式地址哈希表的问题是它们的性能会随着时间的推移而降低,尤其是当条目非常动态时。
例如,让我们考虑一个简单的线性探测列表。如果在散列槽1上有3次冲突,则将使用插槽1,2,3。如果2被删除,您需要将其标记为“之前使用过”仍然能够在插槽3中找到该项目。对于某些使用模式,这会使您的散列表降级到线性搜索时间越来越多的点,需要一次代价高昂的改造,使其再次发挥作用。
当插入/删除大量项目时,关闭地址哈希表的性能会随着时间的推移而变得更加稳定。但它们并不像缓存一样友好,因为你必须摆弄指针。
因此,如果您有几乎恒定的密钥,请使用开放寻址,否则请考虑密切寻址的哈希表。
对于某些问题,您可能还想查看其他概念,例如cuckoo哈希。
答案 1 :(得分:0)
无需从线性探测开放寻址哈希表中进行延迟删除。硬删除可以在恒定时间内完成,没有表格降级。维基百科哈希表页面上有伪代码多年。我不知道为什么不再存在,但这里有一个永久链接: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的参考号。我相信这与插入具有完全相同的性能特征,并将表恢复为良好的状态,就像从未添加条目一样。
这种删除方法比使用过的'删除标记并偶尔重新删除所有内容'要好,因为上述方法是固定时间而不是分摊的常量时间。如果您有一个包含一百万个项目的哈希表,您要添加和删除,在“删除标记”方法中,偶尔添加或删除的时间比其之前和之后的时间长一百万倍 - 这不是一个良好的性能特征。