堆排序
本文参考《算法导论》,整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/
堆排序和归并排序一样,时间复杂度为,而且可以实现原址排序:任何时候都只需要常数个额外的元素空间存储临时数据。
1. 最大堆
(二叉)堆是一个数组,可以看成一个近似的完全二叉树。树上的每个结点对应数组中一个元素。除了最底层之外,该树是完全充满的,而且是从左到右填充。表示对的数组A包括两个属性:A.length给出数组元素的个数,A.heap-size表示有多少个堆元素存储在数组中。树的根结点为A[1],给定结点的下标i,则它的父节点、左孩子和又孩子的下标:
二叉堆可分为最大堆与最小堆。在最大堆中,最大堆的性质是除了根结点以外的所有结点i都要满足: 。
在堆排序算法中我们使用的就是最大堆。
2. 维护堆的性质
MAX-HEAPIFY是用于维护最大堆性质的重要过程。
输入:数组 A 和一个下标 i 。
前提:在调用MAX-HEAPIFY的时候,假定根结点为 LEFT(i) 和 RIGHT(i) 的二叉树都是最大堆,但A[i]可能小于其孩子,这就违背了最大堆的性质。
思路:通过让A[i]逐级下降的方式,使得下标为以i为根结点的子树遵循最大堆的性质。
伪代码如下:
,在程序的每一步中,从A[i]、A[LEFT(i)]、和A[RIGHT(i)]中选出最大的,并将其下标存储在largest中。如果A[i]是最大的,那么以i为根结点的子树已经是最大堆,程序结束。否则,最大元素是i的某个孩子结点,则交换A[i]和A[largest]的值。从而使i及其孩子都满足最大堆的性质。在交换后,下标为largest的结点的值是原来的A[i],于是以该结点为根的子树又有可能会违反最大堆的性质。因此需要对该子树递归调用MAX-HEAPIFY。
3. 建堆
可以采用自底向上的方法利用过程MAX-HEAPIFY把一个大小为 n=A.length 的数组A[1…n]转换为最大堆。我们知道,子数组 中的元素都是树的叶结点。每个叶结点可以看成只包含一个元素的堆。建堆的过程BUILD-MAX-HEAP对树中的其他结点都调用一次MAX-HEAPIFY:
4.堆排序算法
伪代码:
说明:首先建堆。
因为数组中的最大元素总在根结点A[1]中,通过把它与A[n]进行互换,我们可以让该元素放到正确的位置。这时候,如果我们从堆中去掉结点n,剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根结点可能会未被最大堆的性质。为了维护最大堆的性质,我们调用MAX-HEAPIFY(A,1),从而在A[1…n]构造一个新的最大堆。堆排序算法不断重复这一过程,直到堆的大小降为2。
HEAPSORT(A)的时间复杂度为,因为建堆为
,加上(n-1)次调用MAX-HEAPIFY。