堆还是比较常用的数据结构
二叉堆也就是以二叉树形式构造的堆
我们知道二叉树可以用数组很方便的实现
所以用数组实现二叉堆也不是很难的事情
首先我们来了解一下二叉堆的性质:非叶子节点的值均不大于(或不小于)其左右孩子的值
如果用二叉堆用数组来实现就是n个元素{k1,k2,k3,...,kn}
ki<=k(2*i)&&ki<=k(2*i+1) 或者 ki>=k(2*i)&&ki>=k(2*i+1) 其中(i=1,2,...,n/2);
如此我们就能很方便的在数组上实现根节点和孩子节点的比较
堆中插入元素,插入元素之后还需要保持堆的性质
堆中取元素的时候一般都是取得堆中最大或者最小的元素
这时我们就可以使用大顶堆来维护取最大值,小顶堆来维护取最小值
堆中取出元素时也需要继续维护堆的性质
以小顶堆的数组 实现为例
插入元素:
1.从堆末尾插入元素
2.比较和其父节点元素的大小
2.1.比父节点小,对元素和父节点的元素执行交换操作,
2.2.比父节点大,结束
3.重复2直到根节点
取出最小元素:
1.取出堆的根节点
2.取出堆的最后一个元素插入到根节点
3.比较根节点和左右孩子的大小
3.1.比左右孩子都大,结束
3.2.只比一个大,交换根节点元素和孩子节点元素
3.3.比两个都大,和最小的那个孩子节点交换
4.重复3直到结束,返回1取出的根节点
下面实现一个小顶堆来维护取最小值。
#include<stdlib.h>
#include<stdio.h>
int heap[1000];//定义堆的大小
int end=0;//指向堆中的最后一个元素并且标记元素的个数
void insert(int a){
int e,p,tmp;//e最后一个元素在数组的位置,p为e的父节点。
heap[++end]=a;//从尾部插入元素
e=end;
//对插入的元素进行上升操作
while(e>1){
p=e>>1;
if(heap[e]<heap[p]){//如果比父节点小就上升
tmp=heap[e];
heap[e]=heap[p];
heap[p]=tmp;
}else break;//比父节点大直接break,小顶堆的性质决定
e=p;
}
}
int getMin(){
int ls,rs,rt=1; //rt指向堆的根节点,ls指向根节点的左孩子,rs指向根节点的右孩子
int res,last,tmp; //res堆中的最小值,也就是堆的根节点,last为堆中的最后一个元素
res=heap[rt]; //取出根节点,所以堆的元素个数需要减一
last=heap[end--]; //得到堆的最后一个元素
heap[rt]=last; //把最后一个元素从根节点插入
//对根节点进行下降操作
while(1){
ls=rt<<1;rs=rt<<1|1;
if(ls>end||rs>end){//如果左孩子或者右孩子指向超出元素的总数,也就是指向空
if(ls<=end&&heap[rt]>heap[ls]){//左孩子没有超出,并且需要交换
tmp=heap[rt];
heap[rt]=heap[ls];
heap[ls]=tmp;
}
break;
}
if(heap[rt]<=heap[ls]&&heap[rt]<=heap[rs])break;//左孩子右孩子都比根节点大,不需要交换
if(heap[rt]>heap[ls]&&heap[rs]>=heap[ls]){//比较根节点和左右孩子,和最小的交换
tmp=heap[rt];
heap[rt]=heap[ls];
heap[ls]=tmp;
rt=ls;
}else{
tmp=heap[rt];
heap[rt]=heap[rs];
heap[rs]=tmp;
rt=rs;
}
}
return res;
}
int main(){
//插入20个数据进行测试一下
int a,i;
for(i=1;i<=20;i++){
a=rand()%100;
printf("%2d ",a);
insert(a);
}
printf("\n");
while(end>0){
printf("%2d ",getMin());
}
printf("\n");
return 0;
}