MAX-HEAPIFY 中的最坏情况:“最坏情况发生在树的底层正好是半满时”

Posted

技术标签:

【中文标题】MAX-HEAPIFY 中的最坏情况:“最坏情况发生在树的底层正好是半满时”【英文标题】:worst case in MAX-HEAPIFY: "the worst case occurs when the bottom level of the tree is exactly half full" 【发布时间】:2011-10-15 03:05:29 【问题描述】:

在CLRS,第三版,第 155 页,给出了在 MAX-HEAPIFY 中,

"the worst case occurs when the bottom level of the tree is exactly half full"  

我猜原因是在这种情况下,Max-Heapify 必须“向下浮动”通过左子树。 但我无法得到的是“为什么半满”? 如果左子树只有一个叶子,Max-Heapify 也可以向下浮动。那么为什么不把这当作最坏的情况呢?

【问题讨论】:

【参考方案1】:

阅读整个上下文:

每个孩子的子树的大小最多为 2n/3 - 最坏的情况发生在树的最后一行正好是半满时

由于T(n)的运行时间是通过树中的元素个数(n)来分析的,并且递归步骤进入其中一个子树,所以我们需要找到一个节点数的上限子树,相对于n,这将产生T(n) = T(max num. nodes in subtree) + O(1)

子树中节点数的最坏情况是最后一行在一侧尽可能满,而在另一侧尽可能空。这称为半满。并且左子树的大小将以2n/3为界。

如果您提议的案例只有几个节点,那么这无关紧要,因为所有基本案例都可以考虑 O(1) 并忽略。

【讨论】:

我正在学习堆,我的大脑几乎爆炸了,想为什么答案不是 n,因为我认为如果树的一侧是空的,最大节点将为 n。所以我在想 n 应该是节点数的上限。如果其他人为同样的问题而苦苦挣扎,那么堆就是一棵几乎完整的二叉树。所以除了最后一个级别之外的任何其他级别都应该是满的。 因为我们对递归T(n) = T(s(n)) + O(1) 感兴趣,所以我们需要找到s(n) = subtree size as a function of n 的最坏情况。说我们“最大化子树的大小”是不正确的(我在与这个问题相关的其他几个答案中看到了这一点)——我们实际上是在最大化比率L/R 其中L 和@987654331 @分别是左右子树的大小。 The worst case of number of nodes in a subtree is when the final row is as full as possible on one side, and as empty as possible on the other. 但是为什么呢?作为 OP,我也有确切的疑问,Max-Heapify can also float down if left subtree has only one leaf. So why not consider this as the worst case ? 对不起,我不清楚。如果可能的话,稍微澄清一下会很有帮助。 @momo 因为只有单个叶子并不能保证它会向下浮动到那个特定的叶子,所以为了安全起见,在最坏的情况下,左子树的叶子应该是满的,而右子树的层次要少一层. 我认为这一切都归结为一个子节点可以拥有的节点总数的比例的问题。在完全二叉堆/树的情况下,左右子树中的节点数量相等,设数量为k。因此节点总数为 1 + k + k = 2k + 1。因此,节点的分数为 k/(2k + 1),当 k -> 无穷大时收敛到 1/2。这个分数小于 2/3。因此,最坏的情况不是完全二进制堆,而是半满二进制堆。【参考方案2】:

我知道已经有一个公认的答案,但对于那些有同样问题但仍然有点困惑(就像我一样)的人,或者不清楚的人——这里有一点更长更详细的解释。

虽然这听起来可能很无聊或多余,但我们必须非常清楚确切的定义,因为通过关注细节 - 很可能当你这样做时,证明事情会变得容易得多。

根据 CLRS 第 6.1 节,(二进制)堆数据结构是一个数组对象,我们可以将其视为几乎完整二叉树

来自***,在一个完全二叉树中,每一层,除了可能是最后一层,都被完全填满,并且最后一个层中的所有节点都尽可能离开尽可能。

此外,来自***,平衡二叉树是一种二叉树结构,其中每个节点的左右子树的高度差不超过 1。

因此,与根相比,左右子树的高度最大可以相差 1。

现在,考虑一棵树T,让左子树的高度=h+1,右子树的高度=h

MAX_HEAPIFY 中最坏的情况是什么?最坏的情况是当我们在试图维护堆属性的同时进行更多的比较和交换。

如果 MAX_HEAPIFY 算法运行并且递归地通过最长路径,那么我们可以考虑可能的最坏情况。

嗯,所有最长的路径都在左子树中(因为它的高度是 h+1)。为什么不是正确的子树?记住这个定义,last层的所有节点都必须尽可能的left

所以,为了获得更多的最长路径,我们应该使 left 子树 FULL(为什么?这样我们就可以有更多的路径可供选择并选择能够提供最坏情况时间)。由于左子树的高度为 h+1,它将有 2^(h+1) 个叶节点,因此从根开始有 2^(h+1) 个最长路径。这是树 T(高度为 h+1)中最长路径的最大可能数。

这是最坏情况下树结构的image。

从上图中,考虑黄色(左)和粉红色(右)子树各有 x 个节点。粉色部分是完整的右子树,黄色部分是不包括最后一层的左子树。

请注意,黄色(左)和粉红色(右)子树的高度均为 h。

现在,从一开始,我们就认为左子树的高度为 h+1 作为一个整体(包括黄色部分和最后一层),如果我可以问一下,我们有多少个节点在最后一层,即黄色部分下方添加,使左子树完全充满?

嗯,黄色部分的最底层有 ⌈x/2⌉ 个节点(具有 n 个节点的树/子树中的叶子总数 = ⌈n/2⌉;验证访问 this 链接) ,现在如果我们向这些节点/叶子中的每一个添加 2 个子节点,则 => 总共添加了 x (≈x) 个节点(如何?⌈x/2⌉ 叶子 * 2 ≈ x 个节点)。

通过这个添加,我们使高度为 h+1 的左子树(高度为 h 的黄色部分 + 最后一层添加)和 FULL,因此满足最坏情况的标准。

由于左子树是FULL,所以整棵树是半满的。

现在,最重要的问题是——我们为什么不在右子树中添加更多节点或添加节点?好吧,那是因为现在如果我们倾向于添加更多节点,则必须将节点添加到右子树中(因为左子树已满),这反过来又会使树更加平衡.现在,随着树开始变得更加平衡,我们倾向于朝着最好的情况而不是最坏的情况前进。

另外,我们总共有多少个节点?

树的总节点数 n = x(来自黄色部分)+ x(来自粉红色部分)+ x(在黄色部分下方的最后一层相加)= 3x

请注意,作为副产品,左子树总共最多包含 2x 个节点,即 2n/3 个节点 (x = n/3)。

【讨论】:

以上是关于MAX-HEAPIFY 中的最坏情况:“最坏情况发生在树的底层正好是半满时”的主要内容,如果未能解决你的问题,请参考以下文章

MAX_HEAPIFY 算法和最坏情况的递归关系

Max-Heapify中最糟糕的情况 - 为什么你得到2n / 3?

unordered_map 的最坏情况是啥?

快排的最坏情况

*Algs4-2.1.19希尔排序的最坏情况-(未证明)

用于 SCC 最坏情况分析的 Tarjan 算法