1. 回顾
根据上一篇DSAA之Leftist Heaps(一)的理论,可以很快写出来相应的函数。
2. 左倾堆的实现
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#define maxsize 10
typedef struct node {
struct node * left;
struct node * right;
int npl;
int key;
} NODE;
typedef NODE * Leftist_Heaps;
Leftist_Heaps merge(Leftist_Heaps L,Leftist_Heaps R);
Leftist_Heaps build_heap(int * x, int size,Leftist_Heaps H);
Leftist_Heaps insert(int key, Leftist_Heaps L);
int delete_min(Leftist_Heaps * H);
void printf_inorder(Leftist_Heaps H);
void printf_postorder(Leftist_Heaps H);
int main(void){
Leftist_Heaps leftheaps=NULL;
int data[maxsize];
int i;
printf("please input your own data:\n");
for(i=0;i<maxsize/2;i++)
scanf("%d",&data[i]);
leftheaps=build_heap(data,maxsize/2,leftheaps);
printf("inorder : ");
printf_inorder(leftheaps);
printf("\n");
printf("postorder : ");
printf_postorder(leftheaps);
printf("\n");
printf("choose a number to insert :\n");
scanf("%d",&data[0]);
leftheaps=insert(data[0],leftheaps);
printf("inorder : ");
printf_inorder(leftheaps);
printf("\n");
printf("postorder : ");
printf_postorder(leftheaps);
printf("\n");
printf("delete_min: %d\n",delete_min(&leftheaps));
printf("inorder : ");
printf_inorder(leftheaps);
printf("\n");
printf("postorder : ");
printf_postorder(leftheaps);
printf("\n");
}
Leftist_Heaps build_heap(int * x, int size,Leftist_Heaps H){
int i;
for(i=0;i<size;i++){
H=insert(x[i],H);
}
return H;
}
Leftist_Heaps insert(int key, Leftist_Heaps L){
Leftist_Heaps R;
if((R=calloc(1,sizeof(NODE))) == NULL)
errx(1,"error in calloc\n");
R->key=key;
return merge(L,R);
}
int delete_min(Leftist_Heaps * H){
Leftist_Heaps L,R;
int key=(*H)->key;
L=(*H)->left;
R=(*H)->right;
free(*H);
*H=merge(L,R);
return key;
}
Leftist_Heaps merge(Leftist_Heaps L,Leftist_Heaps R){
Leftist_Heaps little,big,temp;
//两者合并的树其中有一个为NULL,直接返回另一个,这里也处理了两者都为NULL的情况
if(L == NULL)
return R;
else if (R == NULL)
return L;
//预处理
if(L->key > R->key){
little=R;
big=L;
}
else{
little=L;
big=R;
}
if(little->left == NULL){
//无需判断,直接互换左右子树
little->left=big;
little->npl=0;
return little;
}
//靠近基准
little->right=merge(little->right,big);
//每次需要判断左右子树的npl是否符合左倾堆定义
if(little->left->npl < little->right->npl){
temp=little->left;
little->left=little->right;
little->right=temp;
}
//更新当前节点的npl信息
little->npl=little->right->npl+1;
//一定要返回当前节点
return little;
}
void printf_inorder(Leftist_Heaps H){
if( H != NULL){
printf_inorder(H->left);
printf("%d ",H->key);
printf_inorder(H->right);
}
}
void printf_postorder(Leftist_Heaps H){
if( H != NULL){
printf_postorder(H->left);
printf_postorder(H->right);
printf("%d ",H->key);
}
}
结果如下:
[root@localhost ~]# ./6_2
please input your own data:
0 1 2 3 4
inorder : 3 2 4 0 1
postorder : 3 4 2 1 0
choose a number to insert :
5
inorder : 3 2 4 0 5 1
postorder : 3 4 2 5 1 0
delete_min: 0
inorder : 3 2 4 1 5
postorder : 3 4 2 5 1
[root@localhost ~]#
3. 几个强调的地方
看过DSAA的同学可以明显发现,笔者的处理逻辑和书上不同。笔者是按照前一篇总结的算法,自己撸。但是if(little->left == NULL)
的代码逻辑是笔者进行debug的时候修正的。
首先看看一种处理方式:
little->right=merge(little->right,big);
if(little->left == NULL || little->left->npl < little->right->npl){
temp=little->left;
little->left=little->right;
little->right=temp;
}
if(little->right != NULL)
little->npl=little->right->npl+1;
else
little->npl=0;
如果是在merge
之后处理特殊情况时,代码运行到这里一定保证little
节点的右子树非空,左子树不一定。所以处理逻辑变成还要判断一次是否左子树为空。另外最后更新npl
的时候也要注意处理little->right
为NULL
的情况。
第二种方式如笔者上面的:
if(little->left == NULL){
//无需判断,直接互换左右子树
little->left=big;
little->npl=0;
return little;
}
//靠近基准
little->right=merge(little->right,big);
此时只要处理little->left
为空的情况,就可以了。因为代码运行到判断npl
的逻辑时,一定保证左右子树都不为NULL
。以上都是递归的实现细节考究,而非左倾堆构建的算法细节,毕竟递归只是一种编程手段。