堆排序(摘自算法导论)
(二叉)堆是一个数组,他可以被看成一个近似的完全二叉树。树上的每一个节点对应数组中的一个元素,除了最底层之外,该树是完全填满的,而且是从左向右填充。表示堆的数组A包括两个属性,A.length给出数组元素的个数,A.heap-size表示有多少个堆元素存储在该数组中。也就是说,虽然A[1..A.length]可能都有数据,但只有A[1..A.heap-size]中存放的是堆的有效元素。0<=A.heap-size<=A.length。树的根节点是A[1],这样给定一个节点的下标i,我们很容易计算得到它的父节点、左孩子和右孩子的下标。
PARENT(i)
return[i/2]
LEFT(i)
return 2i
RIGHT(i)
return 2i+1
二叉堆可以分为两种形式:最大堆和最小堆。
在最大堆中,最大堆是指除了根节点之外的所有节点i都要满足
A[PARENT(i)]>=A[i]
也就是说,某个节点的最大值至多与根节点一样大。因此,堆中最大的元素存在根节点中;并且,在任一子树中,该子树所包含的所有节点的值都不大于该子树根节点的值。最小堆的组织方式正好相反
最小堆性质市值除了根节点之外的所有节点i都有
A[PARENT[i]]<=A[i]
在堆排序算法中,我们使用的是最大堆,最小堆通常用于构造优先队列。
如果把堆看成一棵树,我们定义一个堆节点中的节点高度就为该节点到叶子节点最长简单路径上的边数;进而我们可以把堆的高度定义为根节点的高度,既然一个包含n个元素的堆可以看作一个完全二叉树,那么该堆的高度是Olgn
堆的几个过程
- MAX-HEAPIFY 时间复杂度O(lgn),维护最大堆的关键
- BUILD-MAX-HEAP 具有线性时间复杂度,功能是从无序的输入数据数组中构造一个最大堆
- HEAPSORT 时间复杂度O(nlgn) 对一个数组进行原址排序
- MAX-HEAP-INSERT、HEAP-EXTRACT-MAX、HEAP-INCREASE-KEY和HEAP-MAXMUM 时间复杂度为O(lgn),功能是利用堆实现一个优先队列
思考:
1、在高度为h的堆中,元素个数最多和最少为多少
2、一个一排好序的数组是最小堆吗
MAX-HEAPIFY 维护堆的性质
它的输入为一个数组A和下标i,在调用MAX-HEAPIFY的时候,我们假定根节点为LEFT[i]和RIGHT[i]的二叉树都是最大堆,但这时候A[i]可能小于其孩子,这样就违背了最大堆的性质。MAX-HEAPIFY通过让A[i]的值在最大堆中逐级下降,从而使得下标i为根节点的子树重新醉醺最大堆的性质。伪代码如下
MAX-HEAPIFY (A,i)
l = LEFT[i]
r = RIGHT[i]
if l<=A.heap-size and A[l] > A[i]
largest = l
else largest = i
if r<=A.heap-size and A[r] > A[largest]
largest = r
if largest != i
swap A[i] A[largest]
MAX-HEAPIFY(A,largest)
建堆
我们可以用自底向上的方法利用过程MAX-HEAPIFY把一个大小为n=A.length的数组A[1..n]转换为最大堆
BUILD-MAX-HEAP
A.heap-size = A.length
for i = [A.length/2] downto 0
MAX-HEAPIFY (A,i)
堆排序算法
初始时候,堆排序算法利用BUILD-MAX-HEAP将输入数组A[1...n]建成最大堆。因为数组中最大元素总是在根节点A[1]中,通过把它与A[n]进行互换,我们可以让该元素放到正确位置,这时候,我们从堆中去掉节点n(这一操作可以通过减少A.heap-size的值来实现),剩余的节点中,原来的孩子节点仍然是最大堆,而新的根节点可能会违背最大堆的特性。为了维护最大堆的特性,我们要做的是调用MAX-HEAFIFY(A,1),从而在A[1..n-1]上构造一个新的最大堆,堆排序算法会不断重复这一过程,直到堆的大小从n-1降到2
HEAPSORT
BUILD-MAX-HEAP(A)
for i = A.length downto 2
swap A[i] A[1]
A.heap-size = A.heap-size - 1
MAX-HEAPIFY(A,1)
用Java来实现这一过程
public class HeapSort {
public void maxHeapIfy(int[] a, int i, int heapSize) {
int l = 2 * i;
int r = 2 * i + 1;
int largest;
if (l <= heapSize - 1 && a[l] > a[i]) {
largest = l;
} else {
largest = i;
}
if (r <= heapSize - 1 && a[r] > a[largest]) {
largest = r;
}
if (largest != i) {
swap(a, i, largest);
maxHeapIfy(a, largest, heapSize);
}
}
public void swap(int[] a, int i, int j) {
int swap = a[j];
a[j] = a[i];
a[i] = swap;
}
public void buildMaxHeap(int[] a) {
int heapSize = a.length;
for (int i = heapSize / 2; i >= 0; i--) {
maxHeapIfy(a, i, heapSize);
}
}
public void heapSort(int[] a) {
int heapSize = a.length;
buildMaxHeap(a);
for (int i = heapSize - 1; i >= 0; i--) {
swap(a, 0, i);
maxHeapIfy(a, 0, i);
}
}
@Test
public void heapSort() {
int[] a = {1, 4, 2, 3, 9, 7, 8, 10, 14, 16};
heapSort(a);
for (int i : a) {
System.out.print(i + " ");
}
}
}