堆排序算法是建立在堆这种数据结构的基础上,其实堆听着很高端,其实很简单,就是一个二叉树,但是又特殊条件,就是其父节点比孩子节点都大(或都小)的堆称为最大堆(最小堆),瞬间感觉很简单了,最简单的保存方法就是直接用数组来保存。
给出一组数,我们要使用堆排序,首先需要建堆,但是这一组数首先肯定是不满足上面堆的性质的,所以我们需要调整,让他满足堆得性质,变成一个堆,怎么调整呢?拿最大堆来说,就是对于一个节点,我们判断其孩子是否有比父亲节点大的,有的话,交换这两个值,这样父亲就比孩子都大了,当然交换完了之后可能以这个孩子为根节点的子树又不满足对性质了,继续交换直到都满足,为了减少交换的次数,我们可以从离叶子节点最近的节点开始交换,一层一层直到根节点。
比如这样一组数a[]={16,7,3,20,17,8},我们建堆
然后依次调整
这样一个堆就建立起来了。剩下的事情就很简单了
有了堆之后我们很容易发现堆有一个很美好的性质,就是根节点的值闭锁所有的值都大(小),那么我们第 i 次选择根节点,放入数组第【n-i+1】,然后把第【n-i+1】的值放到根节点,调整堆,经过n次之后就是一个有序的了。过程如下1
(图片非原创,有版权问题请联系我删除)
堆排序被归类到选择排序里面,因为其思想和选择排序的思想是一样的,从剩下的数组里面选择一个最大的(最小的),放到排好序的位置,然后继续选择,只是选择排序选择一次的复杂度是O(n),这里选择是O(1),但是选择完了需要调整,调整的复杂度是log(n),所以总的复杂度是O(n*logn)。
其实堆的另一个很常用的用法就是优先队列,所以大家可以看出来优先队列的复杂度了,每插入一个元素是O(logn),取出时O(1),这样大家就可以自己实现优先队列了
代码:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1001000;
int a[N];
void Balance(int x,int n)
{
int l = x+x;
int r = x+x+1;
int mid = x;
if(l<n && a[l]>a[mid])
mid = l;
if(r<n && a[r]>a[mid])
mid = r;
if(x!=mid)
{
swap(a[x],a[mid]);
Balance(mid,n);
}
}
void Heap_Sort(int n)
{
for(int i=n/2-1;i>=0;i--) //首先调整堆
Balance(i,n);
for(int i=n-1;i>=0;i--)
{
swap(a[i],a[0]);
Balance(0,i);
}
}
int main()
{
//freopen("Input.txt","r",stdin);
int n,k;
while(~scanf("%d%d",&n,&k))
{
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
Heap_Sort(n);
for(int i=0;i<n;i++)
printf("%d ",a[i]);
puts("");
}
return 0;
}
时间: 2024-10-10 04:55:10