哈夫曼树是一种哈夫曼编码形成的树,是由美国数学家赫夫曼 1952 年发明的,所以也称赫夫曼编码。它是为了解决数据存储空间的问题的,压缩存储就是采用了赫夫曼编码,使得数据的存储变得更合理,这样就压缩了空间。举个例子,以下是分数的统计情况
如果是正常程序来判断某个人的成绩等级时,就有
但是如果把比例看作数据的权值(权值其实就是这种数据在所有数据中所占有的比例,
比例越大,权值越大)
,根据权值进行赫夫曼编码就会得到以下的树图
显然这样的树图比上面的效率更高一点。
如何构建一颗哈夫曼树
例如,给定我们以下四个结点 ABCD,他们的权值是 1548
首先我们选取这些结点中权值最小的两个,使他们成为兄弟来自同一个双亲,双亲的权值为这两个结点权值的和,如图所示。
这样之后 A 和 C 就进入哈夫曼树中了,同时衍生出了一个新结点我们暂时称为 A-C,权值为 AC 的和,那么此时还剩下的结点就是 BD 和 A-C,然后再选两个最小的,进行上述操作。
最后剩下 D 和新的结点,然后再重复操作,得到一颗哈夫曼树
程序实现示例
结构定义示例
哈夫曼树结点结构定义示例:
typedef char TYPE; //数据类型
typedef int VType; //权值类型
typedef struct tnode
{
TYPE date; //元素
VType weight; //权值
struct tnode *parents; //双亲结点
struct tnode *lchild; //左孩子
struct tnode *rchild; //右孩子
}TNode;
初始化结点示例
首先要构建好所有结点元素,保存到链表中,按照权值升序排列,当然,方法很多,这只是其中一种生成哈夫曼树的方法而已。
Head *p=create_double_linkedist(); //构建管理链表
TYPE c;
VType n;
printf("请输入建立哈夫曼树的节点数据和权值,输入-1 结束\n");
for(n=0;n<N;n++)
{
scanf("%c %d",&c,&n);
if(n==0)
break;
//构建结点
TNode *HuffTree = (TNode *)malloc(sizeof(TNode));
HuffTree->date = c;
HuffTree->weight = n;
HuffTree->parents = NULL;
HuffTree->lchild = NULL;
HuffTree->rchild = NULL;
insert_double_linkedist(p,HuffTree); //保存到链表中,按照权值升序排列
}
下面是链表的结构
typedef struct node //单链表结构体
{
TNode *date; //哈夫曼树的结点
struct node *next;
struct node *prev;
}Node;
typedef struct head //管理链表结点
{
int num;
struct node *first; //头
struct node *tail; //尾
}Head;
哈夫曼树构建示例
//创建一颗哈夫曼树
TNode *create_hafumantree(Head *p)
{
if(p==NULL || p->num==0) //链表为空或没有结点
return NULL;
TNode *t = NULL;
if(p->num==1)
{
t = p->first->date;
return t;
}
int min1,min2,sum; //最小值,第二小值,和值
while (p->num>1)
{
min1=p->first->date->weight; //赋值最小值
min2=p->first->next->date->weight; //赋值第二小值
sum=min1+min2; //和值
//构建结点保存和值结点
TNode *HuffTree = (TNode *)malloc(sizeof(TNode));
HuffTree->date = 0; //无数据
HuffTree->weight = sum;
HuffTree->parents = NULL;
HuffTree->lchild = p->first->date;
HuffTree->rchild = p->first->next->date;
p->first->date->parents = HuffTree->rchild;
p->first->next->date->parents = HuffTree->rchild;
//最小和第二小的结点出链表
delete_frist(p); //删除头节点
delete_frist(p);
insert_double_linkedist(p,HuffTree); //和值结点按权值升序插入链表
}
t = p->first->date; //最后一个链表节点就是根节点
delete_frist(p); //清空链表
return t;
}