当我遇到有趣的陈述时,我正在阅读HashSet上的javadocs:
此类为基本操作(添加,删除,包含和调整大小)提供了恒定的时间性能。
这使我非常困惑,因为我不知道如何为比较操作获得恒定的时间O(1)。这是我的想法:
如果这是真的,那么无论我将多少数据转储到HashSet中,我都将能够在恒定时间内访问任何元素。也就是说,如果我在HashSet中放入1个元素,则查找该元素所花的时间将与我有一个元素googolplex一样。
但是,如果我有恒定数量的存储桶或一致的哈希函数,这将是不可能的,因为对于任何固定数量的存储桶,该存储桶中的元素数量将线性增长(但是,如果数量很大,则增长缓慢)足够)以及集合中的元素数量。
然后,唯一可行的方法是每次插入元素(或每隔几次)都具有一个变化的哈希函数。一个简单的哈希函数,永远不会发生任何冲突都可以满足此需求。字符串的一个玩具示例可能是:取字符串的ASCII值并将它们连接在一起(因为添加可能会导致冲突)。
但是,对于足够大的字符串或数字等,此哈希函数以及任何其他此类哈希函数可能会失败。可以形成的存储桶数立即受到所拥有的堆栈/堆空间量等的限制。 ,因此无法无限期地跳过内存中的位置,因此最终您将不得不填补空白。
但是,如果在某个时候重新计算了哈希函数,那么这只能与找到经过N个点或O(nlogn)的多项式一样快。
从而引起我的困惑。虽然我相信HashSet可以在O(n / B)的时间内访问元素,其中B是它已决定使用的存储桶数,但我看不到HashSet如何在O( 1次。
注意:本文和本文均未解决我列出的问题。
存储桶的数量是动态的,大约为〜2n,其中n是集合中元素的数量。
2n
n
请注意,HashSet给出的平均和摊销时间性能为O(1),而不是最差的情况。这意味着,我们可能会O(n)不时地遭受手术。 因此,当垃圾箱太拥挤时,我们只需创建一个更大的新数组,然后将元素复制到其中即可。 这会花费n操作成本,并且在集合中的元素数超过时完成操作,2n/2=n因此这意味着此操作的平均成本受限制n/n=1,该常数是一个常数。
HashSet
O(1)
O(n)
2n/2=n
n/n=1
此外,HashMap提供的碰撞次数 平均 也是恒定的。
假设您要添加一个元素x。h(x)被一个元素填充的概率为〜n/2n = 1/2。它被3个元素填充的概率为〜(n/2n)^2 =1/4(对于的大值n),依此类推。 这使您的平均运行时间为1 + 1/2 + 1/4 + 1/8 + ...。由于该总和收敛2,这意味着该操作 平均 需要花费恒定的时间。
x
h(x)
n/2n = 1/2
(n/2n)^2 =1/4
1 + 1/2 + 1/4 + 1/8 + ...
2