最小堆
利用数组建立堆之前,需要知道一些理论知识。
利用数组储存时,从1开始,如果一个结点的下标为i, 那么他的左儿子的下标为2i, 他的右儿子的下标为2i+1.
如果一个结点的下标为j
那么它的父结点的下标为 j/2
特别主意,要从a【1】开始储存。
#include <stdio.h>
int h[101];
int n;
void Swap(int x, int y) //交换h数组中下标为x和y的两个数
{
int tmp = h[x];
h[x] = h[y];
h[y] = tmp;
}
向下调整函数,以该结点向下调整
调整思路为: 判断该结点的左儿子,右儿子是否比它小,如果小就交换。
交换完之后,以交换的那个儿子结点,继续向下交换。直到没有比它小的儿子或该该点已经没有儿子。
void SiftDown(int i);
//创建函数,就是往数组中输入值之后,从最后一个非叶结点开始向下调整,一直到1号根结点
void Creat(void);
//删除最小值,就是将堆顶的元素与堆中最后一个元素交换,堆的大小减一,
//最后返回刚开始的那个堆顶元素。
int deletmin(void);
int main(void)
{
int i, num;
scanf("%d", &num);//结点的总数
for(i = 1; i <= num; i++)
{
scanf("%d", &h[i]);
}
n = num;
Creat();
for(i = 1; i <= num; i++)
printf("%d ", deletmin());//依次删除堆中最小值,并维护堆的性质
//经过调整之后,发现数组h中数已经是有序的,不信可以输出出来看看哟(*^.^*)
return 0;
}
void SiftDown(int i) //从i起点向下调整
{
int t, flag = 0; //flag 位需要调整的标志,当儿子中有比其小的,则需要调整
while(i*2 <= n && flag == 0)
{
if(h[i*2] < h[i])
t = i*2;
else
t = i;
if(h[i*2+1 <= n])
{
if(h[i*2+1] < h[t])
t = i*2+1;
}
if(t != i)
{
Swap(t, i);
i = t;
}
else
flag = 1;
}
}
void Creat(void)
{
int i;
for(i = n/2 ; i >= 1; i--)
SiftDown(i);
}
int deletmin(void) //删除堆顶元素
{
int t;
t = h[1];
h[1] = h[n];
h[n] = t;
n--;
SiftDown(1);
return t;
}