哈夫曼树

一、 哈夫曼树的定义

  设二叉树具有n个带权值的叶子节点,那么根节点到各个叶子节点的路径长度相应节点权值的乘积,叫做叉树的带权路径长度。


其中n表示叶子节点的数目,wi表示叶子节点ki的权值,li表示根到ki之间的路径长度(即从叶子节点到达根节点的分支数)。

具有最小带权路径长度的二叉树称为哈夫曼树,也是最优树,由哈夫曼于1951提出。  

二、 构造哈夫曼树

 根据哈夫曼树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶子节点越靠近根节点,而权值越小的叶子节点越远离根节点。那么如何构造一棵哈夫曼树呢?

其方法如下:

(1)给定的n个权值{W1,W2,...,Wn}构造n棵只有一个根节点的二叉树,从而得到一个二叉树的森林F={T1,T2,...,Tn};

(2)在F中选取根节点的权值最小和次小的两棵二叉树作为左、右子树构造一棵新的二叉树,这棵新的二叉树根节点的权值为其左、右子树根节点权值之和;

(3)在集合F中删除作为左、右子树的两棵二叉树,并将新建立的二叉树加入到集合F中;

(4)重复(2)、(3)两步,当F中只剩下一棵二叉树时,这棵二叉树便是所要建立的哈夫曼树。

用ht[]数组存放哈夫曼树,对于具有n个叶子节点的哈夫曼树,总共有2n-1个节点。树中每个节点结构如下:

typedef struct
{ char data; //节点值
float weight; //权重
int parent; //双亲节点
int lchild; //左孩子节点
int rchild; //右孩子节点

} HTNode; 

其算法思路是:

1.n个叶子节点只有data和weight域值,先将所有2n-1个节点的parent、lchild和rchild域置为初值-1

2.处理每个非叶子节点ht[i](存放在ht[n]~ht[2n-2]中):

从ht[0] ~ht[i-2]中找出根节点(即其parent域为-1)最小的两个节点ht[lnode]和ht[rnode]将它们作为ht[i]的左右子树,ht[lnode]和ht[rnode]的双亲节点置为ht[i],并且ht[i].weight= ht[lnode].weight+ht[rnode].weight。

3.如此这样直到所有2n-1个非叶子节点处理完毕。

void CreateHT(HTNode ht[],int n)
{  int i,j,k,lnode,rnode; float min1,min2;
   for (i=0;i<2*n-1;i++)   //所有节点的相关域置初值-1
      ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
   for (i=n;i<2*n-1;i++) //构造哈夫曼树
   {  min1=min2=32767; lnode=rnode=-1;
for (k=0;k<=i-1;k++)
  if (ht[k].parent==-1) //未构造二叉树的节点中查找
  {  if (ht[k].weight<min1)
     {  min2=min1;rnode=lnode;
  min1=ht[k].weight;lnode=k;  }
     else if (ht[k].weight<min2)
     {  min2=ht[k].weight;rnode=k;  }   
        } //if
  ht[lnode].parent=i;ht[rnode].parent=i;
        ht[i].weight=ht[lnode].weight+ht[rnode].weight;
  ht[i].lchild=lnode;ht[i].rchild=rnode;
   }


三、哈夫曼编码

具体构造方法如下:设需要编码的字符集合为{d1,d2,…,dn},各个字符在电文中出现的次数集合为{w1,w2,…,wn}

以d1,d2,…,dn作为叶节点,以w1,w2,…,wn作为各根节点到每个叶节点的权值构造一棵二叉树。

规定哈夫曼树中的左分支为0,右分支为1,则从根节点到每个叶节点所经过的分支对应的0和1组成的序列便为该节点对应字符的编码。这样的编码称为哈夫曼编码。 

为了实现构造哈夫曼编码的算法,设计存放每个节点哈夫曼编码的类型如下:

 typedef struct
 {  char cd[N];  //存放当前节点的哈夫曼码
    int start;   //存放哈夫曼码在cd中的起始位置

 } HCode;

根据哈夫曼树求对应的哈夫曼编码的算法如下:

void CreateHCode(HTNode ht[],HCode hcd[],int n)
{  int i,f,c; HCode hc;
   for (i=0;i<n;i++) //根据哈夫曼树求哈夫曼编码
   {  hc.start=n;c=i; f=ht[i].parent;
while (f!=-1)   //循环直到无双亲节点即到达树根节点
{  if (ht[f].lchild==c) //当前节点是左孩子节点
      hc.cd[hc.start--]='0';
   else   //当前节点是双亲节点的右孩子节点
      hc.cd[hc.start--]='1';
   c=f;f=ht[f].parent; //再对双亲节点进行同样的操作
  }
hc.start++; //start指向哈夫曼编码最开始字符
       hcd[i]=hc;
   }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值