哈夫曼树是一个很经典的问题,本文输入是一串数字,然后输出生成哈夫曼树的中序遍历结果
#include <stdio.h>
#include <stdlib.h>
typedef struct node//每个树的节点至少包含3个内容,节点的权值,左子树,右子树的地址
{
int element;
struct node *Lchild,*Rchild;
}node;
typedef struct tree
{
struct node *root;
}tree;//每一棵树都是由一个个节点构成的
void Maketree(tree *bt,int x,tree *Lt,tree *Rt)//该函数的目的是传入x的值,建立一棵左子树是Lt,右子树是Rt的树bt(创建方法就是先申请一个空间保存权值,然后记录下左子树右子树的地址,此时注意访问子树有两种方式:
//1直接通过左子树Lt->root来访问2通过p->Lchild来访问,而事实上,我们希望的是第二种方式,即这时左子树已经成为整体的一部分,所以都要清零( Lt->root=Rt->root=NULL;)
{
node *p=(node*)malloc(sizeof(node));
p->element=x;
p->Lchild=Lt->root;
p->Rchild=Rt->root;
Lt->root=Rt->root=NULL;
bt->root=p;//最后把先建立的节点赋值给bt(我们就是通过bt传递的)
}
tree CreatHFMtree(int w[],int n)//传入两个参数,一个是数组w,一个是节点的数量n
{
tree zero,h[1000];
int i,k,k1,k2;
tree *p;
p=&zero;
p->root=NULL;
for (i=0;i<n;i++)
Maketree(&h[i],w[i],p,p);//先建成n个树,再进行合并
for (k=n-1;k>0;k--)//n棵树合并,进行n-1次
{
Fmin(h,&k1,&k2,k);//在已经建成的n棵树中,找出最小值的下标k1,次小值得下标k2,最小值每次只会在前k个值(每一次找出两个值都会在数组中忽略最小值)
printf("%d %d\n",k1,k2);
Maketree(&h[k1],h[k1].root->element+h[k2].root->element,&h[k1],&h[k2]);//这步可以说是哈夫曼树的核心代码,把找到的两个小值求和h[k1]+h[k2],作为h[k2]的树,而他的子树就是h[k1],h[k2]
h[k2]=h[k];//每一次把最小值换成队尾的那个,此时最小值就没有了,而有了两个队尾元素,然后由于k--,队尾元素相当于被删除了一个,所以只剩下了除了最小数的全部元素(如果最小值就是队尾元素,那也是成立,可以自己思考一下)
}
return h[0];
}
void Fmin(tree h[],int *k1,int *k2,int k)//k1是最小值的下标,k2是次小值的下标(这一部分其实就是维护由两个元素组成的堆)
{
int i,min1,min2;
if (h[0].root->element<h[1].root->element)
{
*k1=0;
*k2=1;
min1=h[0].root->element;
min2=h[1].root->element;
}
else
{
*k1=1;
*k2=0;
min1=h[1].root->element;
min2=h[0].root->element;
}
for (i=2;i<=k;i++)
if (h[i].root->element<min1)//如果比最小值还小
{
*k2=*k1;//先更新次小值的为原来的最小值,原来的最小值更新为h[i].root->element
*k1=i;
min2=min1;
min1=h[i].root->element;
}
else
if (h[i].root->element<min2)
{
*k2=i;
min2=h[i].root->element;//直接更新次小值
}
}
void PreOrd(node *t)
{
if (t)
{
printf("%d ",t->element);
PreOrd(t->Lchild);
PreOrd(t->Rchild);
}
}
int main()
{
int w[1000];
int i,n;
tree start;
printf("请输入树节点的个数\n");
scanf("%d",&n);
printf("请输入节点\n");
for (i=0;i<n;i++)
scanf("%d",&w[i]);
start=CreatHFMtree(w,n);
PreOrd(start.root);
printf("\n");
return 0;
}
再来个算法竞赛入门经典中的例子
将建成的树左分岔设为0,右分叉设为1,可以得到书上的编码方式~~