1. 回顾
上一篇记录了优先级堆的一些基本概念,和基本删除插入操作的特点,本篇开始具体实现。DSAA给出了一些实现,笔者做了些改变,舍弃数组[0]为空的策略。
2. 代码展示
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#define element_type int
#define MAX_BUF 30
#define maxsize 10
struct heap_struct{
/* Maximum # that can fit in the heap */
unsigned int max_heap_size;
/* heap中最后一个位置 */
unsigned int size;
element_type *elements;
};
typedef struct heap_struct * PRIORITY_QUEUE;
PRIORITY_QUEUE create_pq( unsigned int max_elements );
void insert( element_type x, PRIORITY_QUEUE H );
void Build_heap(element_type * x, int size,PRIORITY_QUEUE H);
element_type delete_min( PRIORITY_QUEUE H );
int main(){
PRIORITY_QUEUE p_q;
element_type data[MAX_BUF];
int i,j;
printf("please input your own data:\n");
for(i=0;i<maxsize;i++)
scanf("%d",&data[i]);
p_q=create_pq(MAX_BUF);
Build_heap(data,maxsize,p_q);
for(i=0,j=2;i<=p_q->size;i++){
printf("%d ",(p_q->elements)[i]);
if(i== j-2){
printf("\n");
j*=2;
}
}
printf("\n");
printf("choose a number to insert :\n");
scanf("%d",&data[0]);
insert(data[0],p_q);
for(i=0,j=2;i<=p_q->size;i++){
printf("%d ",(p_q->elements)[i]);
if(i== j-2){
printf("\n");
j*=2;
}
}
printf("\n");
printf("first delete_min: %d\n",delete_min(p_q));
for(i=0,j=2;i<=p_q->size;i++){
printf("%d ",(p_q->elements)[i]);
if(i== j-2){
printf("\n");
j*=2;
}
}
printf("\n");
}
PRIORITY_QUEUE create_pq( unsigned int max_elements ){
PRIORITY_QUEUE H;
H = (PRIORITY_QUEUE) malloc ( sizeof (struct heap_struct) );
if( H == NULL )
errx(1,"Out of space!!!");
//这里笔者并没有在数组空出来第一个元素
H->elements = (element_type *) malloc(( max_elements) * sizeof (element_type));
if( H->elements == NULL )
errx(1,"Out of space!!!");
H->max_heap_size = max_elements;
H->size = -1;
return H;
}
void insert( element_type x, PRIORITY_QUEUE H ){
unsigned int i;
if( H->max_heap_size == H->size +1)
errx(1,"Priority queue is full");
else{
//更新当前size,及将i指向新插入的节点的位置
i = ++H->size;
//找当前节点的父代,如果比当前节点大,则需要向上渗透
while( i!=0 && H->elements[(i-1)/2] > x ){
//将当前节点的父代和当前节点的值互换
H->elements[i] = H->elements[(i-1)/2];
//笔者会这么处理:
//tmp = H->elements[i];
//H->elements[i] = H->elements[(i-1)/2];
//H->elements[(i-1)/2]=tmp;
//DSAA的处理比较巧妙,因为x的值是我们一直都知道的
i = (i-1)/2;
//因为笔者不将数组的第一个元素空出来,所以要加下面的语句
}
H->elements[i] = x;
}
}
void Build_heap(element_type * x, int size,PRIORITY_QUEUE H){
int i;
for(i=0;i<size;i++)
insert(x[i],H);
}
element_type delete_min( PRIORITY_QUEUE H ){
unsigned int i, child;
element_type min_element, last_element;
if( H->size == -1 )
errx(1,"Priority queue is empty");
min_element = H->elements[0];
last_element = H->elements[H->size--];
//debug
//printf("last_element %d\n",last_element);
//向下渗透,从i=0开始
for( i=0; i*2+1 <= H->size; ){
//每次找当前节点的子代,有可能是单子代
child = i*2+1;
//第一个逻辑判断是否当了最后一个元素,因为只有最后一个元素才可能有单子代的情况。
//否则判断左右子代的Key值大小,决定渗透的方向
if( ( child != H->size ) &&( H->elements[child+1] < H->elements [child] ) )
//如果是右子代的话,要正确更新chlid,使其指向右子代
child++;
//这里的逻辑,需要仔细思考,如果最后一位元素比当前chlid的值大
//则证明还需要继续给最后一个元素找合适的插入位置。
if( last_element > H->elements[child] ){
//将chlid的元素放到当前i的位置,同时child复制给i,重复上面的工作,此时
//也可以像笔者Inser函数提到的,互换i和child的值,但是我们一直知道最后一
//个元素的值,所以可以直接这么简化
H->elements[i] = H->elements[child];
i=child;
}
else
//直接找到了合适的位置,向下渗透结束,也就是最后一个元素插入到当前的i的位置
break;
}
H->elements[i] = last_element;
return min_element;
}
3. 结果
[root@localhost ~]# ./6_1
please input your own data:
13 21 16 24 31 19 68 65 26 32
13
21 16
24 31 19 68
65 26 32
choose a number to insert :
14 #这是自己输入的数字回显
13
14 16
24 21 19 68
65 26 32 31
first delete_min: 13
14
21 16
24 31 19 68
65 26 32
[root@localhost ~]#
上面两个图,直接验证了插入的正确性,然后删除的正确性需要自己动手画下。笔者之前的打印输出也能直观的看出来,其实也不用画图验证了。最后具体的思考细节详细的记录在代码的批注里面,这里就不赘述了。