堆排序的核心就是如何建堆以及如何保持堆的性质,而建堆也是利用保持堆的性质来实现的,因此最核心的就是如何保持堆的性质,以大根堆为例说明:
大根堆必须满足如下性质:
1.每个结点的值都不小于它的左右子树根节点的值
2.是一个完全二叉树
首先来看,如何保持大根堆的性质?
我们使用 MaxHeapify (int array[], int length, int root)函数来实现,关键思想是:在调用本函数之前,假设以root的子女left[root] 和 right[root] 为根的子树都是大根堆,但是array[root] 可能小于它的子女,这就违背了大根堆的性质,因此找出array[root] 、左右子女3个元素中最大的一个与array[root] 交换,但是交换之后就破坏了原有的左右子树的大根堆,因此递归调用 MaxHeapify 继续保持大根堆的性质,代码如下:
/* * 函数介绍:用来保持大根堆的性质 * 输入参数:数组指针、数组长度 root为数组下标,从0开始计算 * 输出参数:无 * 返回值 :空 */ void MaxHeapify (int array[], int length, int root) { int left = 2*root + 1; int right = 2*root + 2; int largest = root; if ( (left < length) && (array[left] > array[largest]) ) { largest = left; } if( (right < length) && (array[right] > array[largest]) ) { largest = right; } if( largest != root ) { int temp = array[root]; array[root] = array[largest]; array[largest] = temp; MaxHeapify(array, length, largest); } }
利用上述函数就可以自底向上完成建堆的工作,因为完全二叉树的所有叶子节点本身就是大根堆,因此只需要从最靠后的非叶子节点开始,针对每一个结点调用MaxHeapify,这样就完成了建堆的工作。代码如下:
void BuildMaxheap(int array[], int length) { for( int i = length/2 ; i > 0; --i) MaxHeapify(array, length, i-1); }
然后,就可以利用上述函数进行堆排序了。因为大根堆的根就是最大的元素,所以考虑将根与最后的叶子节点交换,但是交换破坏了大根堆,因此需要调用MaxHeapify来保持大根堆的性质,然后再将次大的元素与倒数第二个叶子节点交换,如此反复,就可以得到一个有序序列。代码如下:
/* * 函数介绍:首先建立大根堆,然后将数组第一个元素(最大元素)与数组最后一个元素交换, * 并递减数组长度,调用MaxHeapif保持大根堆的性质 * 输入参数:数组和数组长度 * 输出参数:无 * 返回值 :排好序的数组指针 */ int *HeapSort(int array[], int length) { BuildMaxheap(array, length); for(int i = length ; i > 1; --i) { int temp = array[length-1]; array[length-1] = array[0]; array[0] = temp; --length; MaxHeapify(array, length, 0); } return array; }
注:《算法导论》中所说的数组下标是从1开始算的,所以实现的时候要适当调整,并注意边界条件。
原文:http://blog.csdn.net/xinshen1860/article/details/21345249