假设MAX-HEAPIFY操作。其中父元素值大于其子值。
siftDown交换一个太小的节点与其最大的子节点(从而将其向下移动),直到它至少与两个节点一样大 低于它。
siftUp与其父节点交换一个太大的节点(从而移动 它直到它不大于它上面的节点。 buildHeap function接受一系列未排序的项目并将它们移动到它们之前 都满足堆属性。
buildHeap可能采用两种方法。一个是开始 在堆的顶部(数组的开头)并调用siftUp 每个物品。在每一步,先前筛选的项目(之前的项目 数组中的当前项)形成一个有效的堆,并筛选下一个 item up将它放入堆中的有效位置。筛选后 每个节点,所有项都满足堆属性。第二种方法 走向相反的方向:从阵列的末端开始并移动 向后朝前。在每次迭代中,您都会筛选一个项目 直到它在正确的位置。
让数组a有5个元素a [4,2,3,5,6]。
siftUp -
输入 - [4,2,3,5,6]
处理 -
从数组的开头应用siftUp操作。
siftUp(4) -
没有交换,因为它是根
heapified Array-a[4]
siftUp(2) -
没有交换,因为父值(4)更多
heapified Array-a[4,2]
siftUp(3) -
没有交换,因为父值(4)更多
heapified Array-a[4,2,3]
siftUp(5) -
其父值为2,因此交换(5,2)。
Array-a[4,5,3,2]
现在5的父值是4,所以再次交换(5,4)。
heapified Array-a[5,4,3,2]
siftUp(6) -
首先在(6,4)之间,然后在(6,5)之间交换
heapified Array-a[6,5,3,2,4]
输出一个[6,5,3,2,4]
siftDown -
输入 - [4,2,3,5,6]
处理 -
从数组的末尾开始,我们将逐个应用siftDown操作。
siftDown(6) -
它没有孩子所以没有交换。这同样适用于siftDown(5)和siftDown(3),因为他们没有任何孩子。所以他们不能再往下走了。
直到现在的数组 - [4,2,3,5,6]
siftDown(2) -
它与较大的子值交换。这里6是较大的一个。所以交换(2,6)。
现在阵列将是-a [4,6,3,5,2]
siftDown(4) -
4有两个孩子6和3.与较大的孩子交换。交换(4,6)完成。 现在数组将是[6,4,3,5,2]
再次4必须被交换,因为它有一个大于它自己的子,即5。所以交换(4,5)已经完成。
数组将是 - [6,5,3,4,2]
Heapified数组-a [6,5,3,4,2]
输出-A [6,5,3,4,2]
为什么在同一组元素上执行siftUp和siftDown操作时会得到两个不同的输出?或者,当siftUp和siftDown应用于同一组元素时,可能会有不同的堆。请澄清。 :)
答案 0 :(得分:2)
是的,可以为同一组元素使用不同的堆。
两种方法都正确生成满足堆属性的堆:父元素值大于其子值。
第一种方法:
6
/ \
5 3
/ \
2 4
第二种方法:
6
/ \
5 3
/ \
4 2
事实上,如果你手工绘制它,还有其他可能的堆,例如
6
/ \
4 5
/ \
2 3
但请注意,这两种方法虽然产生了正确的结果,但它们具有不同的复杂性。请参阅How can building a heap be O(n) time complexity?。
答案 1 :(得分:1)
您描述的第一种方法(自上而下)不是从未排序数组构建堆的常规方法,可能是因为它需要O(n log n)时间!这是因为它意味着必须在底层执行筛选操作(底层大小为n / 2,其深度为log-n)。
正常的方法是对阵列进行自下而上的扫描,并对每个节点执行大幅下降。每个级别的时间将是级别中的节点数乘以高度(因为sift-down是recusrive并且可能一直交换到底层)。总时间为O(1 * n / 2 + 2 * n / 4 + 3 * n / 8 + ...)= O(n)*(1/2 + 2/4 + 3/8 + .. 。)= O(N)* 2 = O(n)中。
`
1/2 + 2/4 + 3/8 + 4/16 + ... =
1/2 + 1/4 + 1/8 + 1/16 + ... +
+ 1/4 + 1/8 + 1/16 + ... +
+ 1/8 + 1/16 + ... +
+ 1/16 + ... +
+ ... =
1 +
1/2 +
1/4 +
1/8 +
... = 2
`