什么是堆?
堆:是一种特殊的序列 并且 将该序列想象为 完全二叉树
元素满足:
- (ki <= k2i && ki <= k2i+1) 每个结点一定比它的左右孩子小 这种堆称为 最小化堆(小堆) (树根是最小的)
- (ki >= k2i && ki >= k2i+1) 每个点一定比它的左右孩子大 这种堆称为 最大化堆(大堆) (树根是最大的)
堆排序(以最大化堆为例)
堆调整(堆的初始化):排序之前需要将序列调整为 堆 (不满足时 与 左右孩子中较大的那一个进行交换) 从n/2向下取整个点开始由后向前调整直到满足堆条件为止。
排序:
- 从堆顶取出最大元素 ,堆顶元素与堆的最后一个元素交换位置 (往后就不用考虑当前最后一个元素)。
- 对剩余的元素进行调整 , 调整选择剩下元素中的最大元素 (根与左右孩子中较大的那个进行交换直到满足条件为止)即 对根进行堆调整。
- 重复以上过程 直到序列有序为止 (大堆升序)。
例子 (堆调整过程)
初始序列为:5 4 8 0 9 3 2 6 7 1
1.画出该序列完全二叉树
2.找到n/2向下取整的点 本例为 (9)从后往前逐个调整
-
0 < 6 同时 0 < 7 因为 7 > 6 故 0 与 7 交换 ;满足条件 。
8满足条件 继续 4 < 7 同时 4 < 9 因为 9 > 7 故 4 与 9 交换 ;满足条件。
-
5 < 9 同时 5 < 8 因为 9 > 8 故 5 与 9 交换 ;此时 5 < 7 不满足 5与7 交换 ;此时 5 <
6 不满足 5与6 交换;满足条件。 -
堆排序
参考上述排序过程。
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
typedef long long ll;
using namespace std;
const int N = 1010;
int n;
int a[N];
void mheap(int a[],int x,int m)
{
int temp;
while((2*x <= m && a[x] < a[2*x]) || (2*x+1 <= m && a[x] < a[2*x+1])) // 小于左右孩子 并且 防止越界
{
temp = a[2*x] >= a[2*x+1]||2*x == m ? 2*x:2*x+1; // 比较左右孩子取较大的那个,也可能存在只有左孩子
swap(a[x],a[temp]);
x = temp;
}
}
void heapsort(int a[],int len)
{
for(int i = len/2;i >= 1;i--)
mheap(a,i,len); //对n/2向下取整由后往前调整
for(int j = len;j >= 2;j--)
{
swap(a[1],a[j]);
mheap(a,1,j - 1); //对根节点调整
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
cin >> n;
for(int i = 1;i <= n;i++) cin >> a[i];
heapsort(a,n);
for(int i = 1;i <= n;i++)
cout << a[i] <<' ';
return 0;
}