我试着看http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-4-heaps-and-heap-sort/来了解堆和堆,但没有发现这一点。
我不明白max-heapify的功能。它似乎是一个递归函数,但是由于树的高度,它被称为以对数时间运行。
对我来说这毫无意义。在最坏的情况下,它不是必须反转每个节点吗?我不知道如果不重复地触及每个节点,如何做到这一点。
答案 0 :(得分:1)
在最坏的情况下,它不是必须反转每个节点吗?
您不必经历每个节点。标准max-heapify
算法是:(取自维基百科)
Max-Heapify (A, i):
left ← 2*i // ← means "assignment"
right ← 2*i + 1
largest ← i
if left ≤ heap_length[A] and A[left] > A[largest] then:
largest ← left
if right ≤ heap_length[A] and A[right] > A[largest] then:
largest ← right
if largest ≠ i then:
swap A[i] and A[largest]
Max-Heapify(A, largest)
您可以看到,在每次递归调用中,您都可以停止或继续使用子树left
或right
。在后一种情况下,使用1
减小树高。由于堆树是按照定义进行平衡的,因此最多可以执行log(N)
步骤。
答案 1 :(得分:1)
这是MAX-HEAPIFY的作用:
给定索引 i 的节点,其左右子树是max-heaps,MAX-HEAPIFY将 i 的节点向下移动到最大堆,直到它不再为止违反了max-heap属性(即节点不小于其子节点)。
节点在正确位置之前可以采用的最长路径等于节点的起始高度。每次节点需要在树中再向下一级时,算法将选择一个分支,并且永远不会回溯。如果正在堆积的节点是最大堆的根,那么它可以采用的最长路径是树的高度,或O(log n)
。
MAX-HEAPIFY只移动一个节点。如果要将数组转换为最大堆,则必须确保所有子树都是max-heaps,然后再转到根目录。您可以通过在n/2
个节点上调用MAX-HEAPIFY来执行此操作(叶子始终满足最大堆属性)。
来自CLRS:
for i = floor(length(A)/2) downto 1
do MAX-HEAPIFY(A,i)
由于您调用MAX-HEAPIFY O(n)
次,因此构建整个堆为O(n log n)
。
答案 2 :(得分:0)
这是为什么它是 O(N) 的一个论点。
假设它是一个完整的堆,所以每个非叶子节点都有两个孩子。 (即使情况并非如此,它仍然有效,但更烦人。)
在树的每个节点上放一枚硬币。每次我们进行交换时,我们都会花费其中一个硬币。 (请注意,当元素在堆中交换时,硬币不会与它们交换。)如果我们运行 MAX-HEAPIFY,并且还有剩余的硬币,这意味着我们进行的交换比树中的节点少,因此 MAX-HEAPIFY 执行 O(N) 交换。
声明:在 MAX-HEAPIFY 运行完成后,堆将始终至少有一条从根到叶子的路径,路径的每个节点上都有硬币。
归纳证明:对于单节点堆,我们不需要做任何交换,所以我们不需要花费任何硬币。因此,一个节点可以保留它的硬币,并且我们有一条从根到叶(长度为 1)的完整路径,硬币完好无损。
现在,假设我们有一个包含左子堆和右子堆的堆,并且 MAX-HEAPIFY 已经在这两个子堆上运行。通过归纳假设,每条路至少有一条从根到叶子的路径,上面有硬币,所以我们至少有两条有硬币的根到叶子路径,每个孩子一条。为了建立 MAX-HEAP 属性,根需要走的最远是一直交换到树的底部。假设它向下交换到左子树,并一直向下交换到底部。对于每次交换,我们都需要花费硬币,所以我们从根交换到的节点花费它。
这样做时,我们将所有硬币都花在了一个从根到叶的路径上,但请记住,我们最初至少有两个!因此,在整个堆上运行 MAX-HEAPIFY 之后,我们仍然有一个完整的从根到叶的路径,其中包含硬币。因此,MAX-HEAPIFY 花费的硬币比树中的节点少。因此,交换次数为 O(N)。 QED。