堆可以定义为一颗二叉树,树中每个节点对应一个键值,并且满足以下两个条件:其一树是完全二叉树,其二是每个结点的键值都小于等于它的两个子女的键值。(有的教材说是大于,本质上是不变的)。关于堆的解释及算法推导这里不做解释,可以参考:
http://blog.youkuaiyun.com/xiaoxiaoxuewen/article/details/7570621/
对于调整处序号为k的调整算法,称之为筛选算法,通过筛选算法,可以将任意一个排序码序列建成一个堆,堆的第一个元素,即完全二叉树的根节点是排序码中最小的,将选出的排序码从堆中删除,对剩余的部分重新建堆,可以继续选出其中的最小者,直到剩余一个元素排序即告结束。
而删除完全二叉树中的根节点重新建堆,不需要从头再来,有效的方法是将当前堆中的最后一个元素和根节点交换位置,同时让堆中元素减一,因为此时根节点的左右结点都还满足堆的条件,所以可以从根节点处利用筛选算法继续调整建堆。
具体的例子请看下面:
#include <stdio.h>
#include <stdlib.h>
//筛选算法,k为调整位置,m为堆的大小
void sift(int *a,int k,int m)
{
int i,j,finished;
i=k;
j=2*i;
a[0]=a[k]; //a[0]空间空出,用来存放要调整位置的值
finished=0;
while((j<=m)&&(!finished))
{
if ((j<m)&&(a[j+1]<a[j])) //求出较小的点,用j标记
j++;
if (a[0]<=a[j]) finished =1; //满足堆的条件,跳出循环
else
{
a[i]=a[j]; //交换使三个中最小的点放在堆顶点
i=j;
j=2*j; //继续调整儿子是堆顶点的堆,使其满足堆的条件。
}
}
a[i]=a[0];
}
void heapsort(int *a)
{
int i;
for(i=5;i>=1;i--)
{
sift(a,i,10); //for循环完成了对所有元素建堆,方式是从下往上。
}
for (i=10;i>=2;i--) //i表示当前堆的大小,即等待排序的元素的个数
{
a[0]=a[i];
a[i]=a[1];
a[1]=a[0]; //将堆中最小元素与最后一个元素进行交换
sift(a,1,i-1); //从根节点处继续调整建堆
}
}
int main()
{
int a[10]={0,1,3,5,7,9,8,6,4,2};
int i=0;
heapsort(a);
for(i=1;i<10;i++)
{
printf("%d ",a[i]);
}
return 0;
}