我对算法理论很感兴趣(来自Cormen) 本章中有一个关于二进制尝试的练习,请求:
可以使用min-heap属性打印出n节点的密钥 在O(n)时间排序的树?展示如何,或解释原因。
我认为是可能的。
在最小堆中,节点中的元素小于其子节点
因此堆的根始终是所有n个元素中较小的元素,并且根的左子节点小于左子树中的所有元素,并且根的右子节点小于所有元素中的所有元素。正确的子树等。
因此,如果我们继续使用root,打印它,然后使用较小的子进程更新root,我们保留min-heap属性,并按排序顺序打印。 (我在考虑一个不基于数组的最小堆)。
所以这可以在O(n)时间内完成,因为更新根,我们只是比较2个子节点并将根指针更新为2中的较小者。
但我在解决方案中检查了这一点:
Cormen Supplement Solutions
1)它谈论max-sheaps 2)它说它不能在O(n)时间内完成:
在堆中,节点的密钥是其子密钥。在二进制文件中 搜索树,节点的键是它的左子键,但是它的右键 孩子的钥匙。堆属性,与binary-searth-tree不同 属性,不帮助按排序顺序打印节点,因为它 不会告诉节点的哪个子树包含要打印的元素 在该节点之前。在堆中,最小元素小于节点 可以在任何子树中。请注意,如果堆属性可以 用于在O(n)时间内按排序顺序打印键,我们将有一个 O(n)-time算法用于排序,因为只构建堆 准时。但我们知道(第8章)必须进行比较 (n lg n)时间。
从我的观点来看,我可以理解使用max-heap,无法在O(n)中打印它们。
但是,根据我解释的推理,是不是可以使用min-heap属性来做呢?
另外,为什么解决方案会忽略min-heap。是拼写错误还是错误?
我在这里误会了什么吗?
答案 0 :(得分:8)
首先,在讨论中遗漏min-sheaps可能不是一个错字,如果我们讨论的是最小堆或最大堆(比较器刚刚反转),这并不重要。
仅提取根然后替换其两个子节点中较小的一个的问题是,不保证左子节点小于右子树中的所有节点(反之亦然)。考虑以下堆
1
/ \
4 6
/\ /\
5 8 9 7
打印1
后,您必须重新合并,也就是说您提取1
并将其替换为最后一行中的最后一个元素,在本例中为7
。然后,只要您需要将堆返回到正确的状态
take away root and last node to root
7
/ \
4 6
/\ /
5 8 9
swap
4
/ \
7 6
/\ /
5 8 9
swap
4
/ \
5 6
/\ /
7 8 9
所有这些交换都需要花费log n
时间。
如果您改为使用4
替换根节点,您仍然必须通过左分支重新定义结构,从而增加提取根节点的线性成本的成本。如果堆看起来像这样
1
/ \
4 9
/\ /\
5 6 11 15
/\
8 7
我看到形成解决方案的页面
2)Wolfram MathWorld: heap这里的堆特别有助于理解为什么它不是线性操作。
答案 1 :(得分:2)
考虑最小堆的数组表示。您在根目录中拥有最小值。提取根并将其替换为最后一个数组元素,即最后一个叶子位于叶子的最低不完整“行”中。执行MIN-HEAPIFY操作(与CLRS MAX-HEAPIFY相同,但反向比较)。这需要O(log n),结果是根的第二个最小元素。重复直到堆为空。这给出了一个排序的序列。
因此算法的复杂性
log (n) + log (n-1) + log (n-2) + ... + 1 <= n*log n
即。 O(n * log n)
这是预期的,否则,我们会得到一个基于比较的排序,其复杂度小于O(nlogn),这是不可能的。
答案 2 :(得分:0)
我猜你的想法基本上是一堆(考虑一个最小堆),它的根是最小的元素。现在对于第二个最小元素,左右子树都具有最小堆属性,因此我们可以简单地比较左右子节点以找到第二个最小元素。同样可以继续....所以它的O(n)? 你忽略的一件事是,每个级别要比较的元素数量也在增加...... 最小的 - 0比较(根是最小的) 为第二个最小 - 1比较(左树的根或右树的根) 让我们说左树根小于右树根节点。 为第三小 - 2比较。 (右树的根或左子树的两个子中的任一个)。 在计算渐近时间复杂度时,您忽略了这个比较部分。