为什么在实现优先级队列时使用堆而不是二叉树?

时间:2013-03-26 19:36:51

标签: java collections queue binary-tree

在我看来,堆超过二叉树的唯一优势是在二进制树中以O(1)而不是O(log(2)n)的复杂度找到堆中的最小项。

实现优先级队列时,您需要从数据结构中删除每个最小的项目。从树中删除最小的项,并且两个堆都以O(log(2)n)的复杂度完成。 Althogh从树中删除项目可能更复杂。删除没有孩子的项目非常简单。

我的问题是为什么在实现优先级队列时使用堆而不是二叉树(在这种情况下更简单)?

5 个答案:

答案 0 :(得分:11)

当二叉树收敛到数组时,二进制树情况下的最坏情况复杂度为O(n),而在堆中它仍为O(log(n))。你可以使用平衡的二进制树,如红黑或AVl,但随后它变得越来越复杂,需要更多的内存。

答案 1 :(得分:5)

堆通常更简单来实现,而不是正确平衡的二叉树。此外,它们需要更少的内存开销(元素可以直接存储在数组中,而不必分配树节点和指针以及所有内容),可能更快的性能(主要是由于使用单个连续数组的内存局部性)...为什么不会你使用它们吗?

答案 2 :(得分:5)

您的首选应取决于预期的访问模式,以及您可能存储的数据量:...

  • 如果没有太多数据(比如说n小于30),未排序的数组就可以了;
  • 如果您几乎从不添加,删除或更新,排序的数组就可以了;
  • 如果n小于,比如100万,那么你只是在寻找顶级元素 (排名第一,或最后一个),堆将很好 (特别是如果你经常更新随机选择的元素,就像你一样 例如,因为平均而言这是一个缓存的LRU(最近最少使用的)队列 update是O(1),而不是O(log(n)))
  • 如果n小于,比方说,100万,你不确定你会搜索什么 因为,平衡的树(比如红黑或AVL)会很好;
  • 如果n很大(比如说100万及以上),你可能会更喜欢用b树或者 一个特里(一旦n足够大,平衡二叉树的性能趋于“从悬崖上掉下来”:内存访问往往过于分散,缓存未命中真的开始受到伤害)

...但我建议您尽可能将选项保持为开放状态,这样您就可以至少对其中一个选项进行基准测试并切换到它,如果效果更好的话。

在过去的二十年里,我只参与了两个应用程序,其中堆是最好的选择(一次用于LRU,一次用于令人讨厌的操作 - 研究应用程序,恢复对随机扰动的k维超立方体的可加性,其中超立方体中的大多数细胞出现在k个不同的堆中,并且存储器是非常宝贵的)。然而,在这两种情况下,它们的表现远远超过其他选择:比平衡树或b树快几十倍。

对于我在上一段中提到的超立方体问题,我的团队负责人认为红黑树的表现比堆好,但基准测试显示红黑树的速度比较慢(我记得,它们是关于慢了二十倍),虽然b树明显更快,但是它们也很舒服地击败它们。

在我上面提到的两种情况下,堆的重要特征不是O(1)查找最小值,而是随机选择的元素的O(1)平均更新时间。

-James Barbetti(嗯,我以为我是。但验证码一直告诉我,我不是人类)

答案 3 :(得分:0)

首先,有不同的二叉树(其中一些很难,其中一些只提供平均O(log n)),堆就是其中之一。

第二种:虽然大多数树上的操作都是O(log n),但它们更复杂,存在常数因素。

堆需要不断的额外内存,而树通常需要在每个节点中存储指针。

顺便说一下,堆很简单,只使用数组(我不确定如果它是用Java实现的,但我确实这么认为)

答案 4 :(得分:0)

如果您经常使用查找或搜索操作,则首选平衡二叉树。由于这个原因,线段交叉代码使用平衡树而不是堆。