这里的堆不是内存管理中的堆,对于堆排序中的堆,它是一种数组,同时我们也可以将其视作一颗完全二叉树,树中的每个结点号与数组下标相对应,结点值为结点号对应数组下标的数组元素值,我们的排序对象是数组元素,但是排序思想是从完全二叉树中得来的
对于堆,可将其分为大根堆和小根堆,大根堆的特性是除了根节点以外的每个节点i,都有
A[PARENT[i]] >= A[i],即每个节点的值至多和其父节点的值一样大,对于小根堆,反过来即可。
在堆排序中,使用是的大根堆,下面说明如何在一个堆中保持大根堆的性质
保持大根堆的子程序为max_heapify,它的输入为一个数组和下标i,以及数组元素个数n,当max_heapify被调用时,我们假定以LEFT(i)和RIGHT(i)(LEFT(i)和RIGHT(i)分别是节点i 的左右孩子的下标,如果数组下标以0开始,则LEFT(i)返回2*i+1,RIGHT(i)返回2*i+2)为根的两棵二叉树都是最大堆,但是A[i]可能小于其子女,max_heapify函数的功能就是让A[i]在最大堆中下降,使得以i为根的子树成为最大堆
C语言实现算法如下
4 void max_heapify(int *array,int i,int n){
5 int r = i*2+2;
6 int l = i*2+1;
7 int max;
8 int temp;
9 if(l<n&&array[l]>array[i])
10 max = l;
11 else
12 max = i;
13 if(r<n&&array[r]>array[max])
14 max = r;
15 if(max != i){
16 temp = array[i];
17 array[i] = array[max];
18 array[max] = temp;
19 max_heapify(array,max,n);
20 }
21 }
注意,在第19行,这里递归调用函数是因为如果A[i]和其左孩子或者右孩子交换值的话,交换过后以左右孩子为根的子树又有可能违反最大堆性质,所以要对孩子子树递归调用max_heapify
该子算法的的运行时间是调整A[i],A[LEFT[i]]和A[RIGHT[i]]的关系时所用的时间O(1),以及在某个子树上递归调用max_heapify所消耗的时间,时间复杂度为O(lgn)
下面我们可以在上述子算法的基础上建堆,对于一个n个元素的数组A[0-(n-1)],子数组A[n/2-(n-1)]在完全二叉树中都是叶子节点,所有这些叶子节点都可以看作是只含有一个元素的堆,对于其他每一个节点,我们都调用一次max_heapify即可,建堆的算法C语言实现如下
22 void build_max_heap(int *array,int n){26 }
23 int i;
24 for(i=n/2-1;i>=0;i--)
25 max_heapify(array,i,n);
在创建完一个大根堆后,接下来就是对大根堆进行排序,由建堆过程知,数组中最大的元素为A[0],我们可以将此元素和A[n-1]交换位置,将最大的元素放置在最终位置上,然后我们将数组元素个数调整为n-1(由于最大的元素已经在最终的位置上,我们可以将其去掉),对于这n-1个元素的数组,很容易将其调整为最大堆,新的根元素很可能会违反最大堆性质,我们只需要调用一次max_heapify即可,堆排序不断重复这个过程,堆的大小从n-1变换到1,C语言实现如下
27 void heapsort(int array[],int n){
28 int i;
29 int temp;
30 build_max_heap(array,n);
31 for(i = n-1;i != 0;i--){
32 temp = array[i];
33 array[i] = array[0];
34 array[0] = temp;
35 n--;
36 max_heapify(array,0,n);
37 }
38 }
下面是整个堆排序代码,在main中先输入数组的大小,然后输入数组元素的值,最后函数对排序后的数组进行输出并释放malloc的内存空间,在整个的堆排序过程中,建堆的过程需要消耗时间O(n),n-1次调用max_heapify所需要消耗的时间是O(lgn)*(n-1),所以整个堆排序的时间复杂度为O(n*lgn)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <malloc.h>
4 void max_heapify(int *array,int i,int n){
5 int r = i*2+2;
6 int l = i*2+1;
7 int max;
8 int temp;
9 if(l<n&&array[l]>array[i])
10 max = l;
11 else
12 max = i;
13 if(r<n&&array[r]>array[max])
14 max = r;
15 if(max != i){
16 temp = array[i];
17 array[i] = array[max];
18 array[max] = temp;
19 max_heapify(array,max,n);
20 }
21 }
22 void build_max_heap(int *array,int n){
23 int i;
24 for(i=n/2-1;i>=0;i--)
25 max_heapify(array,i,n);
26 }
27 void heapsort(int array[],int n){
28 int i;
29 int temp;
30 build_max_heap(array,n);
31 for(i = n-1;i != 0;i--){
32 temp = array[i];
33 array[i] = array[0];
34 array[0] = temp;
35 n--;
36 max_heapify(array,0,n);
37 }
38 }
39 int main(){
40 int n;
41 int value;
42 scanf("%d",&n);
43 int *array=(int *)malloc(sizeof(int)*n);
44 int i=0;
45 while(i<n&&scanf("%d",&value)){
46 array[i] = value;
47 i++;
48 }
49 heapsort(array,n);
50 for(i=0;i<n;i++)
51 printf("%d ",array[i]);
52 printf("\n");
53 free(array);
54 return 0;
55 }