第十二篇:Huffman树&&Huffman编码(附完整源码)

Huffman Tree简介

    赫夫曼树(Huffman Tree),又称最优二叉树,是一类带权路径长度最短的树。假设有n个权值{w1,w2,...,wn},如果构造一棵有n个叶子节点的二叉树,而这n个叶子节点的权值是{w1,w2,...,wn},则所构造出的带权路径长度最小的二叉树就被称为赫夫曼树。

    这里补充下树的带权路径长度的概念。树的带权路径长度指树中所有叶子节点到根节点的路径长度与该叶子节点权值的乘积之和,如果在一棵二叉树中共有n个叶子节点,用Wi表示第i个叶子节点的权值,Li表示第i个也叶子节点到根节点的路径长度,则该二叉树的带权路径长度 WPL=W1*L1 + W2*L2 + ... Wn*Ln。

    根据节点的个数以及权值的不同,赫夫曼树的形状也各不相同,赫夫曼树具有如下特性:

  • 对于同一组权值,所能得到的赫夫曼树不一定是唯一的。
  • 赫夫曼树的左右子树可以互换,因为这并不影响树的带权路径长度。
  • 带权值的节点都是叶子节点,不带权值的节点都是某棵子二叉树的根节点。
  • 权值越大的节点越靠近赫夫曼树的根节点,权值越小的节点越远离赫夫曼树的根节点。
  • 赫夫曼树中只有叶子节点和度为2的节点,没有度为1的节点。
  • 一棵有n个叶子节点的赫夫曼树共有2n-1个节点。

Huffman Tree的构建

    赫夫曼树的构建步骤如下:
    1、将给定的n个权值看做n棵只有根节点(无左右孩子)的二叉树,组成一个集合HT,每棵树的权值为该节点的权值。
    2、从集合HT中选出2棵权值最小的二叉树,组成一棵新的二叉树,其权值为这2棵二叉树的权值之和。
    3、将步骤2中选出的2棵二叉树从集合HT中删去,同时将步骤2中新得到的二叉树加入到集合HT中。
    4、重复步骤2和步骤3,直到集合HT中只含一棵树,这棵树便是赫夫曼树。

    假如给定如下5个权值:


    则按照以上步骤,可以构造出如下面左图所示的赫夫曼树,当然也可能构造出如下面右图所示的赫夫曼树,这并不是唯一的。

            


Huffman编码

    赫夫曼树的应用十分广泛,比如众所周知的在通信电文中的应用。在等传送电文时,我们希望电文的总长尽可能短,因此可以对每个字符设计长度不等的编码,让电文中出现较多的字符采用尽可能短的编码。为了保证在译码时不出现歧义,我们可以采取如下图所示的编码方式:
            

    即左分支编码为字符0,右分支编码为字符1,将从根节点到叶子节点的路径上分支字符组成的字符串作为叶子节点字符的编码,这便是赫夫曼编码。我们根据上面左图可以得到各叶子节点的赫夫曼编码如下:
    权值为5的也自己节点的赫夫曼编码为:11
    权值为4的也自己节点的赫夫曼编码为:10
    权值为3的也自己节点的赫夫曼编码为:00
    权值为2的也自己节点的赫夫曼编码为:011
    权值为1的也自己节点的赫夫曼编码为:010

    而对于上面右图,则可以得到各叶子节点的赫夫曼编码如下:
    权值为5的也自己节点的赫夫曼编码为:00
    权值为4的也自己节点的赫夫曼编码为:01
    权值为3的也自己节点的赫夫曼编码为:10
    权值为2的也自己节点的赫夫曼编码为:110
    权值为1的也自己节点的赫夫曼编码为:111
    

Huffman编码的C实现

    由于赫夫曼树中没有度为1的节点,则一棵具有n个叶子节点的的赫夫曼树共有2n-1个节点(最后一条特性),因此可以将这些节点存储在大小为2n-1的一维数组中。我们可以用以下数据结构来表示赫夫曼树和赫夫曼编码:
[cpp]  view plain   copy
  1. /* 
  2. 赫夫曼树的存储结构,它也是一种二叉树结构, 
  3. 这种存储结构既适合表示树,也适合表示森林。 
  4. */  
  5. typedef struct Node  
  6. {  
  7.     int weight;                //权值  
  8.     int parent;                //父节点的序号,为-1的是根节点  
  9.     int lchild,rchild;         //左右孩子节点的序号,为-1的是叶子节点  
  10. }HTNode,*HuffmanTree;          //用来存储赫夫曼树中的所有节点  
  11. typedef char **HuffmanCode;    //用来存储每个叶子节点的赫夫曼编码  
    根据赫夫曼树的构建步骤,我们可以写出构建赫夫曼树的代码如下:
[cpp]  view plain   copy
  1. /* 
  2. 根据给定的n个权值构造一棵赫夫曼树,wet中存放n个权值 
  3. */  
  4. HuffmanTree create_HuffmanTree(int *wet,int n)  
  5. {  
  6.     //一棵有n个叶子节点的赫夫曼树共有2n-1个节点  
  7.     int total = 2*n-1;  
  8.     HuffmanTree HT = (HuffmanTree)malloc(total*sizeof(HTNode));  
  9.     if(!HT)  
  10.     {  
  11.         printf("HuffmanTree malloc faild!");  
  12.         exit(-1);  
  13.     }  
  14.     int i;  
  15.   
  16.     //以下初始化序号全部用-1表示,  
  17.     //这样在编码函数中进行循环判断parent或lchild或rchild的序号时,  
  18.     //不会与HT数组中的任何一个下标混淆  
  19.   
  20.     //HT[0],HT[1]...HT[n-1]中存放需要编码的n个叶子节点  
  21.     for(i=0;i<n;i++)  
  22.     {  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值