我什么时候要使用堆?


91

除了优先队列的明显答案外,什么时候堆对我的编程冒险有用?


8
这里有很多很好的答案,因此我将以“当堆不够大时”为准。
Don Werve

Answers:


121

每当您需要快速访问最大(或最小)项目时都可以使用它,因为该项目将始终是数组中的第一个元素或在树的根部。

但是,数组的其余部分保持部分未排序。因此,仅即时访问最大(最小)的物品。插入速度很快,因此这是处理传入事件或数据并始终可以访问最早/最大的一种好方法。

对于优先级队列,调度程序(需要最早的项目)等有用。

堆是一棵树,其中父节点的值大于其任何后代节点的值。

如果您将堆看作是按深度按线性顺序存储的二叉树,则首先是根节点(然后是该节点的子节点,然后是这些节点的子节点);那么索引为N的节点的子节点位于2N + 1和2N + 2。此属性允许按索引快速访问。而且由于通过交换节点来操纵堆,因此可以进行就地排序。


3
还要注意,它可以是一种方便的,有保证的NlogN排序,不需要额外的数组分配
溢出

38
知道面试问题的堆也很
高兴

21
@ vivekian2我在这里的唯一原因是因为我即将接受采访。
byxor

为什么不使用带有辅助堆栈的堆栈类来跟踪最大(或最小)的项目。检索为O(1)
Ridhwaan Shakeel

@RidhwaanShakeel如果使用堆栈,则您的物品将始终放在头部,如果需要根据物品的某些属性(例如最大事件)(基于参加活动的人数)来确定物品的位置,该怎么办?
SandeepGodara

49

堆是用于允许快速访问最小或最大结构的结构

但是,为什么要这样呢?您可以只检查add上的每个条目,以查看它是最小还是最大。这样,您始终可以始终拥有最小或最大的时间O(1)

答案是因为堆使您可以拉最小或最大并快速了解NEXT的最小或最大。这就是为什么它被称为优先队列。

现实世界的例子(虽然不是很公平的世界):

假设您有一家医院,根据患者的年龄对其进行护理。年龄最大的人总是被优先参加,无论他/她何时排队。

您不能只跟踪最老的一个,因为如果您将他/她拉出,您将不知道下一个最老的。为了解决该医院问题,您实现了max heap。根据定义,该堆是部分排序的。这意味着您无法按年龄对患者进行排序,但是您知道年龄最大的患者总是排在最前面,因此您可以在固定时间内将患者拉出O(1)并重新平衡日志时间O(log N)

更复杂的示例:

假设您有一个整数序列,并且想要跟踪median。中位数是位于有序数组中间的数字。

例:

[1, 2, 5, 7, 23, 27, 31]

在上述情况下,7是中位数,因为包含较小数字的数组[1, 2, 5]的大小与包含较大数字的数组的大小相同[23, 27, 31]。通常,如果数组中元素的数量为奇数,则中位数为中间2个元素的算术平均值,例如(5 + 7)/2

现在,您如何跟踪中位数? 通过具有2个堆,一个最小堆包含的数字小于当前中位数,而一个最大堆包含的数字大于当前中位数。现在,如果这些堆始终保持平衡,则2个堆将包含相同数量的元素,或者一个堆比另一个堆多1个元素,最多。

在序列中添加新元素时,如果数字小于当前中位数,则将其添加到最小堆中,否则,将其添加到最大堆中。现在,如果堆不平衡(一个堆比另一个堆多一个元素),则从最大堆中拉出一个元素,然后添加到最小堆中。现在他们已经很平衡了。


6
更复杂的例子是惊人的!我一定会尝试一下。但是,我认为您的示例中有一个小错误。由于我们需要通过对中间的两个元素求平均值来获得中位数。我假设我们需要使用最小堆来存储大于当前中位数的数字,并使用最大堆来存储小于当前中位数的数字。这样,我们可以在恒定时间内提取中间的两个元素并计算中位数?我对么?
Calvin Ku

12

堆的特征是它是一种将数据保持半序的结构。因此,在维持完整订单的成本和搜索随机混乱的成本之间进行权衡是一个很好的选择。该特性可用于许多算法,例如选择,排序或分类。

堆的另一个有用特性是可以从数组就地创建堆!


3

也适合选择算法(找到最小值或最大值)


3

在对临时列表进行排序时,应该随时考虑堆。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.