Hashtable getkType键的摊销时间复杂度如何为O1而不是Olog n?

问题描述

在最简单的哈希表实现中,通常通过以下方式检索与键关联的索引:

size++;
int hash = hashcode(key);
int index = hash % size;

对于任意键,我们可以说索引将是[0,size - 1]范围内的整数,每个结果的概率相同。下表列出了添加N个元素后的前5个索引的概率。

Index            |   0              1               2              3                4
--------------------------------------------------------------------------------------------
Probabilities    |  1
                 |  1/2             1/2
                 |  1/3             1/3             1/3
                 |  1/4             1/4             1/4             1/4
                 |  1/5             1/5             1/5             1/5             1/5    
                 |                                    ...
                 |  1/N             1/N             1/N             1/N             1/N
____________________________________________________________________________________________
Total            |  H(N)         H(N) - 1        H(N) - 1.5      H(N) - 1.83     H(N) - 2.08

H(N)描述了应该在索引0处的链中收集多少个元素。此后,每个链在统计上应该具有更少的元素。

H(N)也是直到和包括第N个项的谐波序列的值。尽管没有通用的封闭形式来描述谐波序列,但是可以使用以下公式非常精确地估算该值。

H(N) ≈ ln(N) + 0.5772156649 + 1 / (2N) - 1 / (12N^2)

参考:https://math.stackexchange.com/questions/496116/is-there-a-partial-sum-formula-for-the-harmonic-series

“近似”部分可以归因于ln(N) + 0.5772156649之后的项。 ln(N)是最大的函数,因此摊销的时间复杂度应为 O(log n)

有什么我想念的吗?非常感谢您在这里进行澄清。

解决方法

将我的评论扩展为答案-让我们从这里开始:

在最简单的哈希表实现中,通常通过以下方式检索与键关联的索引:

size++;
int hash = hashcode(key);
int index = hash % size;

实际上并不是大多数哈希表的实现方式。而是,大多数哈希表使用如下策略:

  1. 选择一些固定的起始插槽数量(例如8、16等)
  2. 添加元素后,请将其放入插槽hashcode(key) % tablesize中。
  3. 表中项目数与插槽数之比超过称为 负载系数 的阈值后,请执行 哈希 :将表的大小加倍,并通过使用新的表大小重新计算hashcode(key) % tablesize来重新分配现有元素。 (最后一步确保在调整表大小的情况下仍可以找到项目,并确保将项目分布在整个表中,而不仅仅是前几个插槽。)

这有多快的确切分析将取决于您如何实现哈希表。如果您使用链式哈希(每个项目都放入插槽中,然后存储在包含该插槽中所有项目的单独数组或链表中),并且您的哈希函数“或多或少”是统一随机的,则(直观上)这些项目可能会或多或少地均匀分布在各个表位上。您可以通过假设您确实具有随机哈希函数来对此进行形式分析,在这种情况下,任何一个插槽中的预期项目数最多为表的负载因子(项目数与插槽数之比) )。通常将负载因子选择为一个常数,这意味着每个插槽的预期项目数由该常数上限,因此可以得出有关O(1)预期查找时间的声明。 (您也可以为线性探测哈希表的查找的预期成本获得相似的O(1)界限,但是数学涉及更多。)

“摊销”部分是由于重新哈希处理步骤而出现的。这个想法是,在大多数情况下,插入操作不会使负载因子超过重新哈希所需的阈值,因此它们的速度非常快。但是有时您确实必须重建表。假设您将表的大小增加了一倍,那么您可以显示出每个重新哈希都以线性插入进行,而这些插入不会触发重新哈希,因此您可以将进行重新哈希所需的工作加重到先前的操作中。