n=0;
for(i=1;i<=m;i++)
{
n++;
h[ n]=a[ i]; //或者写成scanf("%d",&h[ n]);
siftup();
}

假设全部的子树都符合最小堆的特性,那么整棵树就是最小堆了。假设这句话没有理解不要着急,继续往下看。
注意全然二叉树有一个性质:最后一个非叶结点是第n/2个结点。





把n个元素建立一个堆,首先我能够将这n个结点以自顶向下、从左到右的方式从1到n编码。这样就能够把这n个结点转换成为一棵全然二叉树。
紧接着从最后一个非叶结点(结点编号为n/2)開始到根结点(结点编号为1)。逐个扫描全部的结点。依据须要将当前结点向下调整。直到以当前结点为根结点的子树符合堆的特性。
尽管讲起来起来非常复杂,可是实现起来却非常easy,仅仅有两行代码例如以下:
for(i=n/2;i>=1;i--)
siftdown(i);
//删除最大的元素
int deletemax()
{
int t;
t=h[ 1];//用一个暂时变量记录堆顶点的值
h[ 1]=h[ n];//将堆得最后一个点赋值到堆顶
n--;//堆的元素降低1
siftdown(1);//向下调整
return t;//返回之前记录的堆得顶点的最大值
}
#include <stdio.h>
int h[ 101];//用来存放堆的数组
int n;//用来存储堆中元素的个数,也就是堆的大小
//交换函数。用来交换堆中的两个元素的值
void swap(int x,int y)
{
int t;
t=h[ x];
h[ x]=h[ y];
h[ y]=t;
}
//向下调整函数
void siftdown(int i) //传入一个须要向下调整的结点编号i,这里传入1,即从堆的顶点開始向下调整
{
int t,flag=0;//flag用来标记是否须要继续向下调整
//当i结点有儿子的时候(事实上是至少有左儿子的情况下)而且有须要继续调整的时候循环窒执行
while( i*2<=n && flag==0 )
{
//首先推断他和他左儿子的关系,并用t记录值较小的结点编号
if( h[ i] > h[ i*2] )
t=i*2;
else
t=i;
//假设他有右儿子的情况下,再对右儿子进行讨论
if(i*2+1 <= n)
{
//假设右儿子的值更小,更新较小的结点编号
if(h[ t] > h[ i*2+1])
t=i*2+1;
}
//假设发现最小的结点编号不是自己,说明子结点中有比父结点更小的
if(t!=i)
{
swap(t,i);//交换它们。注意swap函数须要自己来写
i=t;//更新i为刚才与它交换的儿子结点的编号,便于接下来继续向下调整
}
else
flag=1;//则否说明当前的父结点已经比两个子结点都要小了,不须要在进行调整了
}
}
//建立堆的函数
void creat()
{
int i;
//从最后一个非叶结点到第1个结点依次进行向上调整
for(i=n/2;i>=1;i--)
{
siftdown(i);
}
}
//删除最大的元素
int deletemax()
{
int t;
t=h[ 1];//用一个暂时变量记录堆顶点的值
h[ 1]=h[ n];//将堆得最后一个点赋值到堆顶
n--;//堆的元素降低1
siftdown(1);//向下调整
return t;//返回之前记录的堆得顶点的最大值
}
int main()
{
int i,num;
//读入数的个数
scanf("%d",&num);
for(i=1;i<=num;i++)
scanf("%d",&h[ i]);
n=num;
//建堆
creat();
//删除顶部元素。连续删除n次,事实上夜就是从大到小把数输出来
for(i=1;i<=num;i++)
printf("%d ",deletemax());
getchar();
getchar();
return 0;
}
因此我们将h[ 1]和h[ n]交换,此时h[ n]就是数组中的最大的元素。请注意,交换后还需将h[ 1]向下调整以保持堆的特性。
OK如今最大的元素已经归位。须要将堆的大小减 1 即 n-- ,然后再将 h[ 1] 和 h[ n] 交换,并将 h[ 1] 向下调整。如此重复。直到堆的大小变成 1 为止。此时数组h中的数就已经是排序好的了。代码例如以下:
//堆排序
void heapsort()
{
while(n>1)
{
swap(1,n);
n--;
siftdown(1);
}
}
#include <stdio.h>
int h[ 101];//用来存放堆的数组
int n;//用来存储堆中元素的个数,也就是堆的大小
//交换函数,用来交换堆中的两个元素的值
void swap(int x,int y)
{
int t;
t=h[ x];
h[ x]=h[ y];
h[ y]=t;
}
//向下调整函数
void siftdown(int i) //传入一个须要向下调整的结点编号i。这里传入1。即从堆的顶点開始向下调整
{
int t,flag=0;//flag用来标记是否须要继续向下调整
//当i结点有儿子的时候(事实上是至少有左儿子的情况下)而且有须要继续调整的时候循环窒执行
while( i*2<=n && flag==0 )
{
//首先推断他和他左儿子的关系。并用t记录值较大的结点编号
if( h[ i] < h[ i*2] )
t=i*2;
else
t=i;
//假设他有右儿子的情况下,再对右儿子进行讨论
if(i*2+1 <= n)
{
//假设右儿子的值更大,更新较小的结点编号
if(h[ t] < h[ i*2+1])
t=i*2+1;
}
//假设发现最大的结点编号不是自己,说明子结点中有比父结点更大的
if(t!=i)
{
swap(t,i);//交换它们,注意swap函数须要自己来写
i=t;//更新i为刚才与它交换的儿子结点的编号,便于接下来继续向下调整
}
else
flag=1;//则否说明当前的父结点已经比两个子结点都要大了,不须要在进行调整了
}
}
//建立堆的函数
void creat()
{
int i;
//从最后一个非叶结点到第1个结点依次进行向上调整
for(i=n/2;i>=1;i--)
{
siftdown(i);
}
}
//堆排序
void heapsort()
{
while(n>1)
{
swap(1,n);
n--;
siftdown(1);
}
}
int main()
{
int i,num;
//读入n个数
scanf("%d",&num);
for(i=1;i<=num;i++)
scanf("%d",&h[ i]);
n=num;
//建堆
creat();
//堆排序
heapsort();
//输出
for(i=1;i<=num;i++)
printf("%d ",h[ i]);
getchar();
getchar();
return 0;
}
堆还常常被用来求
一个数列中第 K 大的数。仅仅须要建立一个大小为 K 的最小堆。堆顶就是第 K 大的数。假设求一个数列中第 K 小的数。仅仅最须要建立一个大小为 K 的最大堆。堆顶就是第 K 小的数,这样的方法的时间复杂度是 O(NlogK) 。当然你也能够用堆来求前 K 大的数和前 K 小的数。你还能想出更快的算法吗?有兴趣的同学能够去阅读《编程之美》第二章第五节。同年,由
Robert W . Floyd 提出了建立堆的线性时间算法。算法》的朋友要去买一本搜藏哦 这年头写个东西不easy。多谢大家支持啦
。当当网购买链接 http://product.dangdang.com/23490849.html