实现
- 相关知识
- 堆:堆是一棵完全二叉树,堆中某个节点的值总是不大于或不小于其父节点的值;
- 最大堆(大根堆):根节点最大的堆;
- 最小堆(小根堆):根节点最小的堆;
- 如果将一维数组看作是一棵顺序存储的完全二叉树,则具有以下关系:
- 对于任意一个父节点,假设其下标为x,则左子(如果有)下标为 (2x + 1), 右子(如果有)下标为(2x + 2)。
- 堆排序:一种选择排序。将具有 n 个元素的数组,调整成堆(最大堆或最小堆),取出堆顶元素(放置到数组头或数组尾),然后将剩下 n-1 个元素重新调整为堆,再次取出堆顶元素, 依次类推,最后得到一个排序数组。
- 思路
- 本文例子是建最大堆,升序排列。
- 思路如下:(数组为 a, 元素数为 n)
- 将数组 a 调整为最大堆, 则 a[0] 为堆顶元素(最大值);
- 将堆顶元素 a[0] 放到数组尾( a[0] 与 a[n -1] 交换 );
- 将前 n-1 个元素 重新调整为堆(其实此时只有 a[0] 可能不满足最大堆);
- 将堆顶元素 a[0] 放到数组次尾( a[0] 与 a[n -2] 交换 );
- 以此类推,最后数组升序排列完成;
- 总结,实际仅需要解决两个问题:
- 1 如何将数组代表的完全二叉树调整为堆?
- 2 如何将只有根节点不满足堆的一组元素调整为堆?
- 代码
void adjust_heap(int a[], int n, int s){
int temp = a[s];
int child = 2*s + 1;
while(child < n){
if ((child + 1 < n -1) && (a[child] < a[child + 1])){
child +=1;
}
if (a[s] < a[child]) {
a[s] = a[child];
s = child;
child = 2 * s + 1;
}else {
break;
}
a[s] = temp;
}
}
void build_heap(int a[], int n) {
if (n < 2){
return;
}
int last_f = 0;
if (n % 2 == 0){
last_f = (n - 1) /2;
} else {
last_f = (n - 3) /2;
}
for (int i = last_f; i >=0; --i){
adjust_heap(a, n, last_f);
}
}
void heap_sort(int a[], int n) {
build_heap(a, n);
for (int i = n - 1; i >=0; --i) {
int temp = a[i];
a[i] = a[0];
a[0] = temp;
adjust_heap(a, i, 0);
print(a, n);
}
}
测试
- 代码
#include <iostream>
using namespace std;
void print(int a[], int num) {
for (int i = 0; i < num; ++i) {
cout << a[i] << " ";
}
cout << endl;
}
int main() {
int a[] = {
7, 6, 5, 4, 3, 2, 1};
int n = sizeof(a) / sizeof(a[0]);
print(a, n);
heap_sort(a, n);
print(a, n);
cin.get();
return 0;
}
- 结果
7 6 5 4 3 2 1
6 4 5 1 3 2 7
5 4 2 1 3 6 7
4 3 2 1 5 6 7
3 1 2 4 5 6 7
2 1 3 4 5 6 7
1 2 3 4 5 6 7
1 2 3 4 5 6 7
1 2 3 4 5 6 7