1. 堆数据结构: 堆是一种数据结构。分为大根堆和小根堆。
对堆数组,其分布与一个完全二叉树对应,其下标的几个关系:
当i的取值满足区间[1, n]时; 数组的小标从1开始;
parent(i) = i >> 1;
left(i) = 2*i;
right(i) = 2*i +1;
对于C语言的数组的小标从0开始,稍作调整如下:
parent(i) = ( ( (i) - 1 ) >> 1); // i >= 1;
left(i) = ( ( (i) << 1 ) + 1); // i >= 0;
right(i) = ( ( (i) << 1 ) + 2) // i >= 0;
parent(i) = ( ( (i) - 1 ) >> 1); // i >= 1;
left(i) = ( ( (i) << 1 ) + 1); // i >= 0;
right(i) = ( ( (i) << 1 ) + 2) // i >= 0;
堆的性质:
对于大根堆数组a: 满足: a[parent(i)] >= a[i];
对于小跟堆数组a : 满足: a[parent(i)] <= a[i];
2.堆的构建: 通过堆的调整,完成堆的构建。
堆的构建的两种方式:
1)像插入排序的方式那样进行增量(incremental)求解, 即其满足循环不变式的性质:
总的描述: 数组a[0...j-1]是一个堆,将a[j]加入,通过堆调整,数组a[0...j]也是一个堆。
满足循环不变式的3个条件:
(1)初始化:只有一个a[0]数组a是一个堆。
(2) 保持: 数组a[0...j-1]是一个堆,将a[j]加入,通过堆调整,数组a[0...j]也是一个堆。
(3)终止: 当数组中所有的元素都加入完毕式,整个数组满足堆的性质,是一个堆。
2)合并求解: 以a[i] 为根的拟堆,除a[i] 以外其他所有的元素都满足堆的性质,即除了a[i]以外是以它的两个孩子为根的两个堆。通过堆调整将a[i]加入,形成以a[i]为根的堆。
在数组a中,若数组的元素个数为n,则从a[n/2]、a[n/2 +1] ... a[n-1]满足堆的性质。
满足循环不变式的三个性质:
(1)初始化: 则从a[n/2]、a[n/2 +1] ... a[n-1]满足堆的性质。
(2)保持: 将a[n/2-1] 加入,通过堆调整,从a[n/2-1]、a[n/2]、a[n/2 +1] ... a[n-1]满足堆的性质。
(3)终止:形成以a[0]为根的堆。
算法实现是基于第二种。
3. 堆排序算法: 采用大根堆。首先对数组a[0...n-1]通过2中的方法建立大根堆。
方法: 将a[0...n-1] 中 根元素a[0] 与 a[n-1]交换,则最大元素a[n-1]确定了,
通过堆调整,形成新的堆数组a[0...n-2]. 重复上述过程,最终完成排序。
上述过程是一个循环不变式:
(1)初始化: a[0...n-1] 是个大根堆。
(2)保持:在大根堆a[0...i] 中,将a[i]与a[0]交换确定a[i], 通过调整a[0...i-1]形成新的大根堆。
(3)终止:确定最小的元素a[0].
算法的实现:
/**
* \file heapSort.c
*
* \brief Heap Sort test file
*/
#include <stdio.h>
#include <stdlib.h>
#define MaxLen 100
#define parent(i) (((i)-1)>>1)
#define left(i) (((i)<<1) + 1)
#define right(i) (((i)<<1) + 2)
/**
* \brief exchange the value of tow integers.
*
* \param a integer point.
* \param b integer point.
*/
void exchange(int* a, int* b);
/**
* \brief print an integer array.
*
* \param a integer array point.
* \param len total # of elements of the array.
*/
void printArray(int* a, int len);
/**
* \brief Max(big) root heap adjust without recurrence.
*
* \param a array point.
* \param i element location.
* \param len total # of elements of the array.
*/
void maxHeapify(int* a, int i, int len);
/**
* \brief Min(small) root heap adjust without recurrence.
*
* \param a array point.
* \param i element location.
* \param len total # of elements of the array.
*/
void minHeapify(int* a, int i, int len);
/**
* \brief Max(big) root heap adjust with recurrence.
*
* \param a array point.
* \param i element location.
* \param len total # of elements of the array.
*/
void maxHeapify_r(int* a, int i, int len);
/**
* \brief Min(small) root heap adjust with recurrence.
*
* \param a array point.
* \param i element location.
* \param len total # of elements of the array.
*/
void minHeapify_r(int* a, int i, int len);
/**
* \brief build max(big) root heap.
*
* \param a integer array point.
* \param len total # of elements of the array.
*/
void buildMaxHeap(int* a, int len);
/**
* \brief build min(small) root heap.
*
* \param a integer array point.
* \param len total # of elements of the array.
*/
void buildMinHeap(int* a, int len);
/**
* \brief heap sort in ascending order.
*
* \param a integer array point.
* \param len total # of elements of the array.
*/
void heapSort(int* a, int len);
/**
* \brief heap sort in decending order.
*
* \param a integer array point.
* \param len total # of elements of the array.
*/
void heapSort_reverse(int* a, int len);
int main(int argc, char* argv[])
{
int a[] = {3, 4, 8, 6, 7, 1, 2, 5, 9};
int len = 9;
heapSort(a, len);
//heapSort_reverse(a, len);
printArray(a, len);
return 0;
}
void exchange(int* a, int* b) {
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void printArray(int* a, int len) {
int i;
for(i=0; i<len; i++) {
printf("%d\t", a[i]);
}
printf("\n");
}
void maxHeapify_r(int *a, int i, int len) {
int l;
int r;
int largest;
l = left(i);
r = right(i);
largest = i;
if(l<len && a[largest]<a[l]) largest = l;
if(r<len && a[largest]<a[r]) largest = r;
if(largest!=i) {
exchange(a+largest, a+i);
maxHeapify_r(a, largest, len);
}
}
void maxHeapify(int *a, int i, int len) {
int l;
int r;
int largest;
int tag; //to indicate the changeable.
do{
tag = 0;
l = left(i);
r = right(i);
largest = i;
if(l<len && a[largest]<a[l]) largest = l;
if(r<len && a[largest]<a[r]) largest = r;
if(largest!=i) {
exchange(a+largest, a+i);
i = largest;
tag = 1;
}
}while(tag);
}
void minHeapify_r(int* a, int i, int len) {
int l;
int r;
int least;
l = left(i);
r = right(i);
least = i;
if(l<len && a[least]>a[l]) least = l;
if(r<len && a[least]>a[r]) least = r;
if(least!=i) {
exchange(a+least, a+i);
minHeapify_r(a, least, len);
}
}
void minHeapify(int *a, int i, int len) {
int l;
int r;
int least;
int tag; //to indicate the changeable.
do{
tag = 0;
l = left(i);
r = right(i);
least = i;
if(l<len && a[least]>a[l]) least = l;
if(r<len && a[least]>a[r]) least = r;
if(least!=i) {
exchange(a+least, a+i);
i = least;
tag = 1;
}
}while(tag);
}
void buildMaxHeap(int* a, int len) {
int i;
int half = (len>>1) -1;
for(i=half; i>=0; i--) {
maxHeapify(a, i, len);
//maxHeapify_r(a, i, len);
}
}
void buildMinHeap(int* a, int len) {
int i;
int half = (len>>1) -1;
for(i=half; i>=0; i--) {
minHeapify(a, i, len);
//minHeapify_r(a,i, len);
}
}
void heapSort(int *a, int len) {
int i;
buildMaxHeap(a, len);
//printArray(a, len);
for(i=len-1; i>0; i--) {
exchange(&a[i], &a[0]);
len--;
maxHeapify(a, 0, len);
}
}
void heapSort_reverse(int *a, int len) {
int i;
buildMinHeap(a, len);
//printArray(a, len);
for(i=len-1; i>0; i--) {
exchange(&a[i], &a[0]);
len--;
minHeapify(a, 0, len);
}
}
// usage for priority queue.
int maxHeapMaximum(int* a, int len) {
return a[0];
}
int minHeapMinimum(int* a, int len) {
return a[0];
}
int maxHeapExtractMax(int* a, int* len) {
int max;
if(*len<1) return -1; //heap empty
max = a[0];
a[0] = a[*len-1];
*len--;
maxHeapify(a, 0, *len);
return max;
}
int minHeapExtractMin(int* a, int* len) {
int min;
if(*len<1) return -1; //heap empty
min = a[0];
a[0] = a[*len-1];
*len--;
minHeapify(a, 0, *len);
return min;
}
int maxHeapIncreaseKey(int* a, int i, int len, int key){
if(i>len || key<=a[i]) return -1;
int p;
a[i] = key;
p = parent(i);
while(i>0 && a[i]>a[p]) {
exchange(a+i, a+p);
i = p;
p = parent(i);
}
return 0;
}
int minHeapDecreaseKey(int* a, int i, int len, int key){
if(i>len || key>=a[i]) return -1;
int p;
a[i] = key;
p = parent(i);
while(i>0 && a[i]<a[p]) {
exchange(a+i, a+p);
i = p;
p = parent(i);
}
return 0;
}
int maxHeapInsert(int* a, int *len, int key) {
int i;
int p;
*len += 1;
if(*len > MaxLen) return -1;
i = *len - 1;
p = parent(i);
a[i] = key;
while(i>0 && a[i]>a[p]) {
exchange(a+i, a+p);
i = p;
p = parent(i);
}
return 0;
}
int minHeapInsert(int* a, int *len, int key) {
int i;
int p;
*len += 1;
if(*len > MaxLen) return -1;
i = *len - 1;
p = parent(i);
a[i] = key;
while(i>0 && a[i]<a[p]) {
exchange(a+i, a+p);
i = p;
p = parent(i);
}
return 0;
}
int maxHeapDelete(int* a, int i, int* len) {
int p;
if(i> *len ) return -1;
a[i] = a[*len -1];
*len --;
maxHeapify(a, i, *len);
p = parent(i);
while(i>0 && a[i]>a[p]) {
exchange(a+i, a+p);
i = p;
p = parent(i);
}
return 0;
}
int minHeapDelete(int* a, int i, int* len) {
int p;
if(i> *len ) return -1;
a[i] = a[*len -1];
*len --;
minHeapify(a, i, *len);
p = parent(i);
while(i>0 && a[i]<a[p]) {
exchange(a+i, a+p);
i = p;
p = parent(i);
}
return 0;
}
4. 用堆数据结构构造优先级队列。
5.用堆构造的优先级队列构造栈、和队列数据结构。
6. 算法分析
堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。 堆排序的最坏时间复杂度为 O(nlogn) 。堆序的平均性能较接近于最坏性能。 由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。 堆排序是就地排序,辅助空间为O(1), 它是 不稳定 的排序方法。
以上参见《算法导论》的第6章。。。