堆還是比較常用的數據結構
二叉堆也就是以二叉樹形式構造的堆
我們知道二叉樹可以用數組很方便的實現
所以用數組實現二叉堆也不是很難的事情
首先我們來了解一下二叉堆的性質:非葉子節點的值均不大於(或不小於)其左右孩子的值
如果用二叉堆用數組來實現就是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;
}