堆排序(HeapSort)是利用堆结构(常用二叉堆)所设计的一种排序算法,属于选择排序的一种。关于堆结构的介绍参见另一篇博文:点击打开链接
算法思想
堆排序利用堆结构根节点的元素最大(或最小)的特点,不断取出根节点并维护堆结构来实现排序。
实现步骤
以大根堆为例:
1.建堆,将初始序列建成一个大根堆
2.将根节点(首元素)与最后一个元素交换
3.堆有序化
4.重复步骤2-3,直到将所有元素都取出
源码
下面的源码与介绍二叉堆的博文一样不使用下标为0的数组元素,若需使用稍加修改即可。
#include<vector>
using namespace vector;
void HeapSort (vector<int>::size_type &vec, vector<int>::size_type N) { //该堆排序要求堆的起始位置是1(0舍弃)
BuildHeap(vec, N);
while (N > 1) {
PopHeap(vec, 1, N);
--N;
}
}
时间复杂度
从源码可以看出,堆排序的主要运行时间消耗在维护堆结构上,而堆是一种树型结构,其比较和交换等基本操作的次数都与树的高度有关。因此不难得出堆排序的时间复杂度为O(nlogn)的线性对数级别。值得注意的是,堆排序和选择排序类似,都是一次循环选择一个元素,但堆排序的比较次数要少得多。
性能特点
堆排序是我们所知的唯一能够同时最优地利用时间和空间的方法——在最坏的情况下它也能保证线性对数级别的时间和空间消耗。当空间十分紧张的时候(例如在嵌入式系统或低成本的移动设备中)它很流行,因为它只用几行就能实现(甚至机器码也是)较好的性能。但现代系统的许多应用很少使用它,因为它无法利用缓存。数组元素很少和相邻的其它元素进行比较,因此缓存未命中的次数要远远高于大多数比较都在相邻元素间进行的算法,如快速排序、归并排序,甚至是希尔排序。
然而另一方面,用堆实现的优先队列在现代应用程序中越来越重要,因为它能在插入操作和删除最大元素操作混合的动态场景中保证对数级别的运行时间。
稳定性
堆排序破坏了相等元素之间的相对次序,属于不稳定的排序算法。
本文部分内容摘自《算法导论(第3版)》和《算法(第4版)》