哈夫曼树:
1.权值越大的节点,距离根越近
2.树中没有度为1的节点,这类树叫正则(严格)二叉树,(树的总节点 = 2*双分支结点+1 = 单分支结点 + 双分支结点 )
3.树的带权路劲(WPL:所有叶子结点的带权长度路劲之和)长度最短。
构造算法:
哔哩哔哩王卓老师的口诀:构造森林全是根,选用两小选新树,删除两小添新人,重复23剩单根
1.将n个权值分别看成N个只有根节点的二叉树,这些树的集合记为F。
2.从F中选出两颗权值最小的树,作为左右子树,构造一棵新的二叉树假设为c,c的权值为a + b的权值,c的左右子树分别为a b。
3.从F中删除a b,加入c
4.继续重复2 3步骤,直到只剩一颗树
#include<stdio.h>
#include<malloc.h>
//单个节点
typedef struct node{
int data;
int weight;
int parent;
int lchild;
int rchild;
}node;
//用ht[]存放哈夫曼树,对于具有n个叶子结点的哈夫曼树,总共有2n-1个节点。
//包含n棵树的森林需要经过n-1次合并才能形成哈夫曼树,共产生n-1个新节点
//ht[]前0-n个数存放原本节点值。
//
void CreateHf(node ht[],int n)
{
int i,j,k,lnode,rnode;
int min1,min2;
//初始化ht[]
for(i=0;i<2*n-1;i++)
{
ht[i].parent=-1;
ht[i].lchild=-1;
ht[i].rchild=-1;
}
for(i=n;i<2*n-1;i++)
{
min1=min2=999999;
lnode=rnode=-1;
for(k=0;k<=i-1;k++)//需要经过n-1次合并。
{
if(ht[k].parent==-1)
{
if(ht[k].weight<min1)//先判断是否是最小的值。
{
min2=min1;
rnode=lnode; //右边的值赋值到左边
min1=ht[k].weight;
lnode=k;//左子树是稍微小点的,即min1是最小的值
}
else if(ht[k].weight<min2)//右子树是权值稍大的,即min2的值
{
min2=ht[k].weight;
rnode=k;
}
}
}
ht[i].weight=ht[lnode].weight+ht[rnode].weight; //i是从n开始的,即创建新节点是从n开始的
ht[i].lchild=lnode;
ht[i].rchild=rnode;
ht[lnode].parent=i;
ht[rnode].parent=i;
}
}
void print(node ht[],int m)
{
printf("权值 双亲 左孩子 右孩子 \n");
for(int i=0;i<2*m-1;i++)
{
printf("%d %d %d %d\n",ht[i].weight,ht[i].parent,ht[i].lchild,ht[i].rchild);
}
}
int main()
{
int m,n,i,j,k;
node ht[200];
while(scanf("%d",&m)!=EOF)
{
for(i=0;i<m;i++)//先将m个权值依次存入ht数组的0~m-1中
{
scanf("%d",&ht[i].weight);
}
CreateHf(ht,m);//构造哈夫曼树
print(ht,m);//输出树
}
}
结果:
哈夫曼编码:
左分支为0,右分子为1
//根据前面的哈夫曼树,构建哈夫曼编码
void seHf(node ht[],int m) {
int i,j,k,t;
j=0;
i=0;
for(i=0; i<m; i++) {
j=0;
t=i;
while(ht[t].parent!=-1) {
if(ht[ht[t].parent].lchild==t)
code[j]='0';
else if(ht[ht[t].parent].rchild==t)
code[j]='1';
j++;
t=ht[t].parent;
}
k=0;
for(j--;j>=0;j--)
{
ht[i].data[k]=code[j];
printf("%c",ht[i].data[k]);
k++;
}
printf("\n");
}
}