试图了解 max heapify
Trying to understand max heapify
我试图通过观看 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的功能。它看起来像一个递归函数,但不知何故,由于树的高度,它以对数时间表示为 运行。
对我来说这毫无意义。在最坏的情况下,它不是必须反转每个节点吗?如果不反复接触每个节点,我看不出如何做到这一点。
In the worst case, won't it have to reverse every single node?
您不必遍历每个节点。标准 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)
步。
MAX-HEAPIFY 的作用如下:
给定索引 i 处的节点,其左右子树为 max-heaps,MAX-HEAPIFY 移动 i[= 处的节点34=] 向下 max-heap 直到它不再违反 max-heap 属性 (即该节点不小于其子节点)。
一个节点在到达正确位置之前可以走的最长路径等于该节点的起始高度。每当节点需要在树中向下移动一级时,算法将恰好选择一个分支进行处理,并且永远不会回溯。如果被堆化的节点是max-heap的根,那么它能走的最长路径就是树的高度,即O(log n)
.
MAX-HEAPIFY 只移动一个节点。如果要将数组转换为 max-heap,则必须确保所有子树都是 max-heaps,然后再转到根。您可以通过在 n/2
节点上调用 MAX-HEAPIFY 来完成此操作(叶子总是满足 max-heap 属性)。
来自 CLRS:
for i = floor(length(A)/2) downto 1
do MAX-HEAPIFY(A,i)
由于您调用 MAX-HEAPIFY O(n)
次,构建整个堆是 O(n log n)
。*
* 如评论中所述,可以显示 O(n) 的更严格的 upper-bound。分析参见CLRS第2版和第3版第6.3节。 (我的第 1 版被打包了,所以我无法验证章节号。)
这是 O(N) 的论据。
假设它是一个完整的堆,所以每个 non-leaf 节点有两个 children。 (即使不是这样它仍然有效,但它更烦人。)
在树的每个节点上放一枚硬币。每次我们进行交换时,我们都会花费其中一个硬币。 (请注意,当元素在堆中交换时,硬币不会与它们交换。)如果我们 运行 MAX-HEAPIFY,并且还有剩余的硬币,这意味着我们进行的交换比那里少是树中的节点,因此 MAX-HEAPIFY 执行 O(N) 次交换。
声明:在MAX-HEAPIFY完成运行ning之后,堆将始终至少有一条从根到叶子的路径,路径的每个节点上都有硬币。
归纳证明:对于single-node堆,我们不需要做任何交换,所以我们不需要花费任何硬币。因此,一个节点可以保留其硬币,并且我们有一条从根到叶子(长度为 1)的完整路径,硬币完好无损。
现在,假设我们有一个包含左子堆和右子堆的堆,并且 MAX-HEAPIFY 已经在两者上 运行。通过归纳假设,每个路径至少有一条从根到叶子的路径,上面有硬币,所以我们至少有 两条 root-to-leaf 条带有硬币的路径,每个 child.为了建立 MAX-HEAP 属性 根需要走的最远的地方就是一直交换到树的底部。假设它向下交换到左子树,并且一直交换到底部。对于每次交换,我们都需要花费硬币,所以我们从根交换到的节点开始花费它。
在这样做的过程中,我们在 root-to-leaf 路径中的 一条 上花费了所有硬币,但请记住我们最初至少有两条!因此,在整个堆上 MAX-HEAPIFY 运行s 之后,我们仍然有一个 root-to-leaf 完整的带有硬币的路径。因此,MAX-HEAPIFY 花费的硬币少于树中的节点数。因此,交换次数为 O(N)。 QED.
我试图通过观看 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的功能。它看起来像一个递归函数,但不知何故,由于树的高度,它以对数时间表示为 运行。
对我来说这毫无意义。在最坏的情况下,它不是必须反转每个节点吗?如果不反复接触每个节点,我看不出如何做到这一点。
In the worst case, won't it have to reverse every single node?
您不必遍历每个节点。标准 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)
步。
MAX-HEAPIFY 的作用如下:
给定索引 i 处的节点,其左右子树为 max-heaps,MAX-HEAPIFY 移动 i[= 处的节点34=] 向下 max-heap 直到它不再违反 max-heap 属性 (即该节点不小于其子节点)。
一个节点在到达正确位置之前可以走的最长路径等于该节点的起始高度。每当节点需要在树中向下移动一级时,算法将恰好选择一个分支进行处理,并且永远不会回溯。如果被堆化的节点是max-heap的根,那么它能走的最长路径就是树的高度,即O(log n)
.
MAX-HEAPIFY 只移动一个节点。如果要将数组转换为 max-heap,则必须确保所有子树都是 max-heaps,然后再转到根。您可以通过在 n/2
节点上调用 MAX-HEAPIFY 来完成此操作(叶子总是满足 max-heap 属性)。
来自 CLRS:
for i = floor(length(A)/2) downto 1
do MAX-HEAPIFY(A,i)
由于您调用 MAX-HEAPIFY O(n)
次,构建整个堆是 O(n log n)
。*
* 如评论中所述,可以显示 O(n) 的更严格的 upper-bound。分析参见CLRS第2版和第3版第6.3节。 (我的第 1 版被打包了,所以我无法验证章节号。)
这是 O(N) 的论据。
假设它是一个完整的堆,所以每个 non-leaf 节点有两个 children。 (即使不是这样它仍然有效,但它更烦人。)
在树的每个节点上放一枚硬币。每次我们进行交换时,我们都会花费其中一个硬币。 (请注意,当元素在堆中交换时,硬币不会与它们交换。)如果我们 运行 MAX-HEAPIFY,并且还有剩余的硬币,这意味着我们进行的交换比那里少是树中的节点,因此 MAX-HEAPIFY 执行 O(N) 次交换。
声明:在MAX-HEAPIFY完成运行ning之后,堆将始终至少有一条从根到叶子的路径,路径的每个节点上都有硬币。
归纳证明:对于single-node堆,我们不需要做任何交换,所以我们不需要花费任何硬币。因此,一个节点可以保留其硬币,并且我们有一条从根到叶子(长度为 1)的完整路径,硬币完好无损。
现在,假设我们有一个包含左子堆和右子堆的堆,并且 MAX-HEAPIFY 已经在两者上 运行。通过归纳假设,每个路径至少有一条从根到叶子的路径,上面有硬币,所以我们至少有 两条 root-to-leaf 条带有硬币的路径,每个 child.为了建立 MAX-HEAP 属性 根需要走的最远的地方就是一直交换到树的底部。假设它向下交换到左子树,并且一直交换到底部。对于每次交换,我们都需要花费硬币,所以我们从根交换到的节点开始花费它。
在这样做的过程中,我们在 root-to-leaf 路径中的 一条 上花费了所有硬币,但请记住我们最初至少有两条!因此,在整个堆上 MAX-HEAPIFY 运行s 之后,我们仍然有一个 root-to-leaf 完整的带有硬币的路径。因此,MAX-HEAPIFY 花费的硬币少于树中的节点数。因此,交换次数为 O(N)。 QED.