21

如果我注意到哈希表(或基于哈希表构建的任何其他数据结构)正在填满,那么您应该在什么时候构建一个包含更多存储桶的新表。到目前为止,给定表中的 n 个项目,您如何计算在新项目中要使用多少个存储桶?

假设我有 100 个桶。当里面有 50 个项目时,我应该重新组织它吗?500?5000?还是我应该寻找最满的桶和钥匙?那么当我达到那个点时,我要制作多大的新哈希表?

与此相关,如果您事先知道大概有多少项将进入,有没有办法计算桶的数量以获得良好的平均性能?

我知道真正的答案取决于许多其他考虑因素,例如在特定示例中速度与大小的重要性,但我正在寻找一般的准则。

我也知道我不应该优化这类事情,除非良好的分析表明这是一个瓶颈。我只是在考虑一个将使用大量哈希表的项目,并想知道如何解决这个问题。

4

5 回答 5

20

一个好的经验法则(并不总是理想的,嗯,只是一个经验法则)是在哈希表填充到 80% 时重新散列。这意味着如果你有 100 个桶和 80 件物品,无论你之前有多少碰撞,都需要时间来增加容量。

你应该增加多少?好吧,也没有完美的价值。最简单的解决方案是每增加一倍容量。所以它会达到 200、400、800 等等。如果您认为这太多了(毕竟当哈希表变得非常大并且您可能永远无法填满 16 MB 时,它会从 8 MB 内存跃升至 16 MB),请选择较小的增长因子。建议至少 1/3(从 100 增长到 133)我会说,作为妥协,也许让它每次增长 50%。

请注意,所有这些还取决于如何处理冲突。处理它们的一种简单方法(我个人最喜欢的)是在发生碰撞时将项目存储在链表中。如果将 3 个项目放在同一个键上,则仍然最多只有 3 次比较才能找到它。由于链表对搜索非常无效,您可能希望提前增加容量,例如,如果使用 60% 的容量来保持哈希表的快速。OTOH,您可以做一些更复杂的事情并保留有关碰撞次数的统计信息。只要你几乎没有任何冲突(如果你有一个非常好的散列函数),就根本不需要重新散列,即使它的 99% 的容量都在使用中。此外,如果您以复杂的方式处理碰撞(例如 每个节点又是一个排序表,您可以在这些表中执行二进制搜索)如果表加载到 200%,您的查找可能仍然足够快(因此您的项目数量是容量的两倍)。在这种情况下,您可以统计最大的排序表有多大,当它大于 8 个条目时,您认为这太慢了,然后您重新散列。

重新散列非常慢,因此应尽可能避免。因此,如果您需要重新散列,不要只是将容量增长得太少,否则在添加更多项目时您必须很快再次重新散列。所以当你需要重新散列时,让容量明显大于当前表中的项目数,其他的都是容量太少。

于 2008-10-22T13:11:07.630 回答
8

通常,您要注意正式定义为 α = n  /  N的负载因子(非正式地,您已经说过),即已 用桶与总桶的比率。为了使哈希表正常运行(或至少从数学角度推断其性能),它应该是 α < 1。

其他一切都取决于经验测试:如果您发现哈希表在 α > 0.5 时表现不佳,请确保保持在该值以下。该值还取决于您的冲突解决技术。使用链式散列可能需要其他负载因子,而不是使用开放寻址的散列。另一个因素是缓存位置。如果您的表变得太大,它将不适合主内存。由于您对数组的访问是随机的,因此从缓存加载可能会成为瓶颈。

于 2008-10-22T13:02:56.900 回答
4

通常有两种类型的哈希表:开放式和封闭式。

在一个开放的哈希表中,您可以根据哈希找到正确的存储桶,然后构建一个挂在该存储桶上的项目列表。

在一个封闭的哈希表中,您可以使用哈希值找到初始存储桶,如果它被占用,您将探测下一个值。在最简单的情况下,您可以通过查找下一个空闲存储桶来执行此操作,或者您可以从您的项目中创建第二个哈希值并逐步进行(尽管您必须确保这是以哈希表大小为模的素数,因此您将访问所有桶)。

打开的哈希表通常不会调整大小。您将初始大小设置为您认为对问题合理的大小。正如其他人指出的那样,您可以在打开的哈希表上调整大小,但是现在推理这种数据结构的性能变得非常困难。如果您在给定存储桶的长度为 L 时调整大小,那么您最终可能只调整整个哈希表中的 L 个项目的大小,这是非常低效的。

当负载因子(哈希表中的项目数/桶数)达到某个预定义值时,将调整关闭的哈希表的大小。我倾向于使用 80%,但确切的值不太可能太关键。

封闭哈希表的好处是插入项目的摊销成本始终为 O(1)(假设哈希函数良好)。由于调整大小的成本,插入特定项目可能是 O(N),但这种情况很少发生。

于 2008-10-22T13:21:20.980 回答
1

取决于您正在构建的哈希表的类型。如果您使用基于固定数组的哈希表(与存储桶的链表相反),则应在表已满或达到最大探测计数时调整数组大小(取决于您是否更关心速度或记忆)。如果您使用的是链表,那么内存就不是那么重要了,因为并且不必探测空白空间,因此调整大小并不是什么大问题。

哈希表的关键是哈希算法,而不是桶的数量。理想情况下,您总是希望每个存储桶中最多有一个项目,因此理想情况下,您应该在哈希表中的项目数 = 存储桶数时调整大小。如果您的数据分布不均,则最好使用更好的哈希算法而不是更好的调整大小策略。

于 2008-10-22T13:15:14.143 回答
1

如果您使用线性散列,则表本身会通过保持恒定的负载因子来自动调整大小。

于 2008-10-29T06:26:04.163 回答