非线性表——哈夫曼树

8.7哈夫曼树

哈夫曼树(最优二叉树):带权路径长度最小的二叉树。

路径:从树中一个结点到另一个结点之间的分支。

路径长度:路径上的分支数。

结点的权:给树的每个结点赋予的实数。

结点的带权路径长度:该结点到根结点之间的路径长度与结点上权的乘积。

树的带权路径长度:

8.7.1构造哈夫曼树

(2)在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和;

(3)在森林中删除这两棵树,同时将新的二叉树加入森林中;

(4)重复上述(2)(3)两步,直到森林中只含一棵树为止,即为哈夫曼树。

例:

有4个结点,权值分别为7,5,2,4,构造有4个叶子结点的哈夫曼树。

解:

 

 

 例:

有8个结点,权值分别为5,29,7,8,14,23,3,11,构造有8个叶子结点的哈夫曼树。

解:

 

8.7.2哈夫曼树的特点

权值最大的叶子结点离根结点最近;

该树上无度为1的结点存在;

一棵有n个叶子结点的Huffman树有2n-1个结点。(因为是二叉树)

例:

对n(n>=2)个权值均不相同的字符构成哈夫曼树,下列关于该哈夫曼树的叙述中,错误的是______。

A.该树一定是一棵完全二叉树  

B.树中一定没有度为1的结点

C.树中两个权值最小的结点一定是兄弟结点

D.树中任一非叶结点的权值一定不小于下一层任一结点的权值

答案:A

8.7.3哈夫曼编码

根据字符出现频率编码,使电文总长最短。

若对每个字符设计长度不等的编码,且出现次数较多的字符尽可能采取短的编码,则可减少总长。

若编码长度不等,则任一字符的编码都不能成为另一字符编码的前缀。

首先,根据字符出现频率构造Huffman树;

然后,结点左孩子的分支标“0”,右孩子的分支标“1”;

每个字符的编码即为从根到每个叶子的路径上得到的0、1序列。

例题:

要传输的字符集D={C,A,S,T,;},字符出现频率 w={2,4,2,3,3},

构造字符的哈夫曼编码,画出最优二叉树,并求编码的平均长度。

注:编码的平均长度为字符出现的概率乘以字符编码长度,再求和。

                                  

 

编码的平均长度=16/7

例题:

电文是{ CAS;CAT;SAT },其编码是?

 解:

11010111011101000011111000

从Huffman树根开始,从待译码电文中逐位取码。若编码是“0”,则向左走;若编码是“1”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束。

例题:

若接收的电文为:“1101000”,对应的原字符序列?

 解:

译文只能是“CAT”

8.7.4哈夫曼算法实现

结点类型:

typedef struct {
   int weight;
   int parent,lchild,rchild;
   //记录父、左右孩子的数组下标
}HTNode,*HuffmanTree;

 

 字符的哈夫曼编码存储类型定义:

typedef char  *HuffmanCode[N+1];

 (1、2、3、4编号对应的是结点7、5、2、4)


 

void HuffmanCoding( HuffmanTree HT,HuffmanCode HC,int *w,int n)

{

  if(n<=1) return;//n为叶子结点
  m=2*n-1;//树中无度为1的结点,因此总结点数为n+n-1
  
  HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
  //1-n号单元存放叶子结点,进行初始化
  for(p=HT+1,i=1;i<=n;++i,++p,++w)  
      *p={ *w,0,0,0 }; //每个结点都有四个域,权值,左右孩子,双亲
  for(;i<=m;++i,++p) *p={ 0,0,0,0 };
      //初始化非叶子结点的值,都设为0

for( i=n+1; i<=m; ++i ) 
{ 
    Select ( HT, i-1, s1,s2 );//在ht[1]到ht[i-1]的范围内选择两个parent为0且weight最小的结点,将序号分别赋值给s1,s2
    HT[s1].parent=i;   HT[s2].parent=i; 
    HT[i].lchild=s1;   HT[i].rchild=s2;   
    HT[i].weight=HT[s1].weight+HT[s2].weight;
}


}

 

 

哈夫曼编码:

//给每一个叶子结点写上哈夫曼编码(01组成的)
HC=(HuffmanCode)malloc((n+1)*sizeof(char *));

cd=(char *)malloc(n*sizeof(char));
cd[n-1]=‘\0’;//cd就是每个叶子结点的哈夫曼编码域

//f是双亲结点,c是当前结点
for( i=1; i<=n; ++i ) //0不用,i是各个叶子结点的标号
{  
    start=n-1;
    for( c=i, f=HT[i].parent;//第一个叶子结点的标号是1
         f!=0;//双亲结点不为0,即还没遍历到最后一个结点(离根最近的结点)
         c=f,f=HT[f].parent )//下一循环是该节点的双亲节点
      
        if( HT[f].lchild==c )  cd[--start]=‘0’;//若该节点是它双亲结点的左孩子
        else   cd[--start]=‘1’;
    
    HC[i]= ( char * ) malloc ( (n-start) *sizeof( char ) );
    strcpy( HC[i], & cd[ start ] );
}

free(cd);

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值