思路
1.建堆(大根堆为例)
(1)从倒数第一个非叶子节点开始,做下沉操作,直到根节点。
(2)下沉操作,对父节点和其儿子节点做比较,若父节点最大,则结束,否则就下沉,然后对发生变化的儿子节点做同样的下沉操作,直到叶子节点。
2.堆排序
(1)将根节点与最后一个叶子节点互换位置;
(2)对根节点做下沉操作。
C++实现
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
/*下沉操作*/
template<class T>
void shiftDown(vector<T>& a, int i, int len, function<bool(const T&, const T&)>cmp) {
int l = 2 * i + 1;
int r = l + 1;
int m = i;
while(l < len) {
if(!cmp(a[l], a[i]) && (r >= len || !cmp(a[l], a[r])) ) {
m = l;
}
else if(r < len && !cmp(a[r], a[i]) && !cmp(a[r], a[l])) {
m = r;
}
if(m != i) { //父节点不是最大的,发生交换
swap(a[m], a[i]);
l = 2 * m + 1;
r = l + 1;
i = m;
}
else {
break;
}
}
}
/*建堆*/
template<class T>
void createHeap(vector<T>& a, int len, function<bool(const T&, const T&)>cmp) {
for(int i = len / 2 - 1; i >= 0; --i) {
shiftDown(a, i, len, cmp);
}
}
/*删除堆顶元素*/
template<class T>
void popHeap(vector<T>& a, int len, function<bool(const T&, const T&)>cmp) {
swap(a[0], a[len - 1]);
shiftDown(a, 0, len - 1, cmp);
}
/*堆排序*/
template<class T>
void heapSort(vector<T>& a, int len, function<bool(const T&, const T&)>cmp) {
createHeap(a, len, cmp);
for(int i = len; i >= 2; --i) {
popHeap(a, i, cmp);
}
}
int main() {
vector<int> a{0, 10, 1, 2, 0, 11, -3};
heapSort<int>(a, a.size(), [](const int& l, const int& r) {
return l < r; //构建大根堆,输出升序序列
});
for(auto i : a) {
cout << i << ' ';
return 0;
}
运行结果:
-3 0 0 1 2 10 11
总结
- 自底向上的建堆复杂度为O(n)
- 堆排序的复杂度为O(nlogn)
- 堆是原地排序,但不稳定