我看到了一些关于 SO re Java hashmaps 及其相关的有趣的声明 O(1) 查找时间。有人可以解释为什么会这样吗?除非这些哈希图与我购买的任何哈希算法有很大不同,否则必须始终存在包含冲突的数据集。

在这种情况下,查找将是 O(n) 而不是 O(1).

有人可以解释一下他们是否 O(1),如果是这样,他们是如何实现这一点的?

有帮助吗?

解决方案

一个HashMap的一个特定特征是,不同的是,比方说,平衡树,其行为是概率性的。在这种情况下,其通常最有帮助谈论的复杂性中出现将是一个最坏的情况下事件的概率的条款。对于一个哈希表,那当然是相对于碰撞到地图恰好有多满是这种情况。碰撞是很容易估算。

  

P <子>碰撞 = N /容量

于是散列映射甚至适度数量的元件是很可能经历至少一个碰撞。大O表示法允许我们做一些更加引人注目。观察到,对于任何任意的,固定的常数k。

  

为O(n)= O(K * n)的

我们可以利用这个特性来提高哈希表的性能。我们可以代替思考最多2和冲突的可能性。

  

P <子>碰撞×2 =(N /容量) 2

此低得多。由于处理一个额外的碰撞成本无关大O性能,我们已经找到一种方法来提高性能,而无需实际改变算法!我们可以generalzie这对

  

P <子>碰撞值X k =(N /容量)ķ

现在,我们可以忽略碰撞的一些任意数量和更多的碰撞比我们占难以察觉的微小可能性告终。你可以通过选择正确的K,而无需改变实际执行的算法得到任意微小级别的概率。

我们谈论这个说的是哈希映射具有O(1)访问的高概率

其他提示

您似乎混合了平均情况(预期)运行时最坏情况下的行为。前者是确实为O(n),用于一般哈希表(即不使用一个完美哈希),但这是在实践中很少有关。

任何可靠的哈希表实现,加上一个半体面散列,具有O(1)具有非常小的因子的检索性能(2,其实)在预期的情况下,方差非常窄的裕度内。

在Java中,HashMap的工作原理是利用的hashCode以定位挖斗。每个桶是居住在该桶的项目清单。该项目被扫描,当使用equals进行比较。当添加的物品,一旦达到一定的负载百分比HashMap中被调整大小。

所以,有时它会进行比较的几个项目,但通常它比为O(n)更接近O(1)。出于实用的目的,这就是你应该需要知道的。

请记住,O(1)并不意味着每个查找仅检查一个单一的项目 - 这意味着项目的平均数目检查保持恒定w.r.t.在集装箱中的物品的数量。所以,如果平均需要4个比较,发现在100项的容器中的项目,也应该采取4个比较平均,以发现在容器中的项目10000项,并为项目的任何其他数(总有一个方差的比特,尤其是在在该哈希表老调重弹,并且点时,有一个非常小数量的项目)。

所以碰撞不会阻止容器从具有O(1)的操作,只要每个桶键的平均数目保持内的固定约束。

我知道这是一个老问题,但实际上有一个新的答案。

您说得对,哈希地图是不是真的O(1),严格来说,因为元素的数量变得任意大,最终你将无法在固定时间进行搜索(和O形符号被定义的数字的,可以得到任意大)。

但它并不意味着真正的时间复杂度为O(n) - 因为没有规定说,该桶必须实现为一个线性表。

在事实上,Java 8实现桶作为TreeMaps一旦超过阈值,这使得实际时间O(log n)

如果(称之为B)保持恒定(通常情况),则查找桶的数目实际上是O(n)。 结果,当n变大,元件的在每个桶平均值N / B的数量。如果冲突解决在通常的方式中的一种(例如链表)完成,然后查找是O(N / B)= O(n)中。

在O符号是关于当n变大时,以及较大的会发生什么。当施加一定的算法也可以是误导性的,和哈希表是一个很好的例子。我们选择基于我们期待有多少元素处理桶的数量。当n是大约相同的尺寸为b,那么查找是大致恒定的时间,但是我们不能称之为O(1)因为O的中的限制当n→∞来定义。

O(1+n/k)其中k是桶的数量。

如果执行集k = n/alpha那么它是O(1+alpha) = O(1)因为alpha是一个常数。

我们已经确定,哈希表查找的标准描述是 O(1),指的是平均情况下的预期时间,而不是严格的最坏情况下的性能。对于通过链接解决冲突的哈希表(如 Java 的哈希图),这在技术上是 O(1+α) 一个好的哈希函数, ,其中 α 是表的负载因子。只要您存储的对象数量不超过表大小的常数因子,该值就保持不变。

还解释说,严格来说,构造需要 O(n) 查找任何确定性哈希函数。但考虑最坏的情况也很有趣 预期的 时间,这与平均搜索时间不同。使用链接,这是 O(1 + 最长链的长度),例如 θ(log n / 日志日志 n)当α=1时。

如果您对实现恒定时间预期最坏情况查找的理论方法感兴趣,您可以阅读 动态完美哈希 它递归地解决与另一个哈希表的冲突!

这是O(1)只有当你的哈希函数是非常好的。 Java的哈希表的实现并不防止坏的哈希函数。

您是否需要添加内容时与否是不相关的问题,增长的表,因为它是关于查找时间。

HashMap中内部元件被存储为链接列表(节点)的阵列,阵列中的每个链接列表表示用于一个或多个密钥的唯一哈希值的铲斗。结果 同时加入在HashMap中的条目,关键的哈希码被用于确定所述阵列中的铲斗的位置,是这样的:

location = (arraylength - 1) & keyhashcode

下面的&代表按位与运算。

例如:100 & "ABC".hashCode() = 64 (location of the bucket for the key "ABC")

在获取操作,它使用相同的方式,以确定键铲斗的位置。在最佳情况下,每个键具有唯一的哈希码,并且导致用于每个键唯一的桶,在这种情况下,get方法仅花费的时间来确定铲斗位置和检索其值是恒定的O(1)。

在最坏的情况下,所有的键具有相同的散列码,并存储在同一个桶中,这导致通过导致至O整个列表遍历(n)的

在的java 8的情况下,该链接的表桶被替换为一个TreeMap如果大小增长到超过8个,这减少了最坏情况下的搜索效率,以O(log n)的

这基本上也适用于大多数编程语言最哈希表的实现,因为算法本身并没有真正改变。

如果没有存在于表碰撞,你只需要做单查找,因此,运行时间为O(1)。如果不存在冲突,你必须做一个以上的查找,这压低了对O(n)的性能。

这取决于您选择,以避免冲突的算法。如果您的实施使用单独的链接那么最坏的情况发生,每一个数据元素被散列为相同的值(例如哈希函数的选择不当)。在这种情况下,数据查找是从链表即O(N)上的线性搜索没有什么不同。然而,这种情况发生的概率是可忽略的和查找最好的和平均的情况下保持恒定,即O(1)。

学术一边,从实用的角度来看,包含HashMap应该被接受为具有无关紧要的影响性能(除非你的分析器告诉你,否则。)

只有在理论情况下,当散列码总是不同的,铲斗每哈希码也不同,在O(1)将存在。否则,它是恒定的顺序即在HashMap中的增量,其搜索的顺序保持不变。

当然的散列映射的性能将取决于基于哈希码()函数的质量给定对象。但是,如果该功能实现,使得冲突的可能性是非常低的,这将有一个非常不错的表现(这不是严格的O(1)的每一个的可能的情况下,但它是在例)。

例如在Oracle JRE的默认的实现是使用随机数(其被存储在对象实例,以便它不会改变 - 但它也将禁用偏向锁,但是这是一个其他的讨论),所以机会碰撞是非常低的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top