改编自于:http://www.cppblog.com/kesalin/archive/2011/03/09/select_sort.html
堆排序
堆的定义:满足如下约束的 n 个关键字序列 Kl,K2,…,Kn 称为堆,1 ≤ i ≤ n/2,
(1) ki ≤ K2i 且 ki ≤ K2i+1 (小根堆) 或
(2) Ki ≥ K2i 且 ki ≥ K2i+1 (大根堆)
从定义来看,堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在的话)结点的关键字。堆排序就是充分利用堆这种堆顶记录要么是最大的要么是最小的有序性质,每次在堆中选择最大或最小关键字完成排序。堆排序也是选择排序的一种,它比直接选择排序平均效率要高,因为直接选择排序,每次比较之后并没有对比较结果进行保存,导致可能存在重复的比较,而堆则利用其堆性质将比较结果保存在堆中了(非叶子节点的左边孩子一定不大于或不小于比右边的孩子)。
堆排序的关键就在于将待排序记录的建立成堆,建好堆之后,将无序区的堆顶记录(总是无序区的最大或最小)和无序区最后一个记录交换,交换之后,无序区的最后一个记录变成有序区的第一个记录,而无序区新的的堆顶记录不一定满足堆性质了,因而需要将无序区调整而堆。如此循环,直到无序区为空,结束排序。
添加的:如果建立一个递增的数组。则应该建立一个大顶堆,然后每次将a[0]与a[n-i]交换
build_heap(int* heap, int length)这个函数的作用就是指定起点:都是从len/2开始,往上走
adjust_heap(int* heap, int low, int high)这个函数的作用就是找出该堆中最大的元素,并且放在堆顶heap_sort(int* array, int length)这个函数的作用就是反复调用上一个函数。将堆顶元素与最低元素交换,别忘了每走一步堆底元素要减1呀~~~~还有一点疑问:for(i = length / 2; i >= 0; --i) { adjust_heap(heap, i, length - 1); }对于下标从0开始的数组来说,i=length/2应该不对呀---例如lenght=11,则i=5;则比较不是结点5的子结点呀???????????
所以堆排序的主要步骤就分三步:
1,建立初始堆;-----找出该堆中最大元素
2,将无序的堆顶记录与最后一个记录交换,缩小无序区;
3,将交换之后的无序区调整为堆,重复步骤 2。
代码实现:
|
时间复杂度分析:
堆排序的时间开销主要由建立初始堆和反复调整堆这两部分的时间开销构成,你可以想象成二叉树的情形。堆排序的平均时间复杂度为O(nlgn)。
空间复杂度分析:
很明显,为 0(1)。
补充:
堆排序是不稳定的就地排序。
=======================================================================================
测试:
运行结果:
=== 堆排序 ===
original: 65 32 49 10 8 72 27 42 18 58 91
sorted: 8 10 18 27 32 42 49 58 65 72 91
original: 10 9 8 7 6 5 4 3 2 1 0
sorted: 0 1 2 3 4 5 6 7 8 9 10