从哈希表中删除条目的最佳方法

Posted

技术标签:

【中文标题】从哈希表中删除条目的最佳方法【英文标题】:Best way to remove an entry from a hash table 【发布时间】:2010-09-21 17:27:37 【问题描述】:

从使用线性探测的哈希表中删除条目的最佳方法是什么?一种方法是使用标志来指示已删除的元素?还有比这更好的方法吗?

【问题讨论】:

【参考方案1】:

一个简单的技巧是:

    查找并删除所需元素 转到下一个存储桶 如果桶是空的,退出 如果存储桶已满,则删除该存储桶中的元素并使用正常方式将其重新添加到哈希表中。必须在重新添加之前移除该项目,因为该项目很可能会被添加回原来的位置。 重复第 2 步。

这种技术可以使您的表保持整洁,但删除速度会稍慢。

【讨论】:

问题是这可能需要一个 O(n) 操作来填补散列表中的空洞,其中 n 是散列表中的槽数。【参考方案2】:

这取决于你如何处理溢出以及(1)被移除的项目是否在溢出槽中,以及(2)如果在被移除的项目之外还有溢出项目,它们是否具有被删除的项目或可能是其他一些哈希键。 [忽略双重条件是删除实现中常见的错误来源。]

如果冲突溢出到链表中,这很容易。您要么弹出列表(可能已为空),要么从链接列表的中间或末尾删除成员。这些很有趣而且不是特别困难。可以进行其他优化来避免过多的内存分配和释放,从而提高效率。

对于线性探测,Knuth 建议一种简单的方法是将插槽标记为空、已删除或已占用。将已删除的占用槽标记为已删除,以便线性探测溢出将跳过它,但如果需要插入,您可以填充您传递的第一个已删除槽 [计算机编程艺术,第 3 卷:排序和搜索,第 6.4 节散列,p。 533 (ed.2)]。这假设删除是相当罕见的。

Knuth 对算法 R6.4 进行了很好的改进 [pp. 533-534],而是将单元格标记为空而不是删除,然后通过移动刚刚制作的孔直到它最终靠近另一个孔,找到将表格条目移回更靠近其初始探测位置的方法。

Knuth 警告说,这将移动现有的仍被占用的插槽条目,如果指向插槽的指针被保留在哈希表之外,这不是一个好主意。 [如果您在插槽中有垃圾收集或其他托管引用,则可以移动插槽,因为它是在表外使用的引用,并且引用的插槽在哪里都没有关系同一个对象在表中。]

【讨论】:

【参考方案3】:

Python 哈希表实现(可以说非常快)使用虚拟元素来标记删除。随着您的增长或缩小或表格(假设您不是在制作固定大小的表格),您可以同时删除假人。

如果您可以访问副本,请查看Beautiful Code 中有关实现的文章。

【讨论】:

【参考方案4】:

我能想到的最佳通用解决方案包括:

如果您可以使用非常量迭代器(ala C++ STL 或 Java),您应该能够在遇到它们时将其删除。不过,大概您不会问这个问题,除非您使用 const 迭代器或枚举器,如果基础集合被修改,它们将失效。 如您所说,您可以在包含的对象中标记已删除的标志。但是,这不会释放任何内存或减少键上的冲突,因此它不是最佳解决方案。还需要在类上添加一个可能并不真正属于那里的属性。如果这让您和我一样困扰,或者如果您根本无法向存储的对象添加标志(也许您不控制类),您可以将这些标志存储在单独的哈希表中。这需要使用最长时间的记忆。 在遍历哈希表的同时,将待删除项的键推入向量或数组列表中。释放枚举器后,循环遍历此二级列表并从哈希表中删除键。如果您有很多要删除的项目和/或密钥很大(它们不应该如此),这可能不是最佳解决方案。 如果您最终要从哈希表中删除的项目多于留在其中的项目,则最好创建一个新的哈希表,并在遍历原始哈希表时添加到新的哈希表中仅列出您要保留的项目。然后将您对旧哈希表的引用替换为新哈希表。这节省了二级列表迭代,但它可能只有在新哈希表的项目比原始哈希表少得多的情况下才有效,当然,它肯定只有在您可以更改对原始哈希表的所有引用时才有效。李> 如果您的哈希表允许您访问其键集合,则您可以遍历这些键并一次性从哈希表中删除项目。 如果您的哈希表或库中的某些帮助程序为您提供了基于谓词的集合修饰符,您可能有一个 Remove() 函数,您可以将 lambda 表达式或函数指针传递给该函数以识别要删除的项目。李>

【讨论】:

【参考方案5】:

当时间是一个因素时,一种常见的技术是创建第二个已删除项目的表,并在您有时间时清理主表。常用于搜索引擎。

【讨论】:

【参考方案6】:

如何增强哈希表以包含像链表这样的指针? 插入时,如果存储桶已满,则创建一个从该存储桶指向存储新字段的存储桶的指针。

从哈希表中删除某些内容时,解决方案将等同于您编写函数从链表中删除节点的方式。

【讨论】:

以上是关于从哈希表中删除条目的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

哈希表:为啥在开放寻址方案中难以删除

开地址哈希表(Hash Table)的原理描述与冲突解决

关于地图中的碰撞

从链式哈希表中有效地挑选一个随机元素?

iOS中的哈希表

哈希表调整大小:我们如何在不知道密钥的情况下做到这一点?