数据结构学习笔记--Huffman树

 
首先介绍什么是Huffman树(译作哈夫曼树或霍夫曼树)。huffman树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶子结点的权值(人为规定)乘上其到根结点的路径长度。树的带权路径长度记为WPL,N个权值Wi(i=1,2,...n)构成一棵有N个叶子结点的二叉树,而huffman树的WPL是最小的。
Huffman 树的一个主要应用是huffman编码,David Huffman在上世纪五十年代初提出了这种编码。根据字符出现的概率来构造平均长度最短的编码。各码字(即为符号经哈夫曼编码后得到的编码)长度严格按照码字所对应符号出现概率的大小的逆序排列。它是一种变长的编码。
为什么huffman编码是前缀编码呢?其实证明很简单,因为所有编码字符都位于huffman树的叶子节点上,任意叶子节点均不是其他叶子节点的祖先,所以对应的编码也就不是其他结点编码的前缀了。
根据选择的存储方式的不同,构造huffman树的算法有许多种(我就找到了8种,汗~)。书上这里使用的是顺序存储的方式,每个结点附有双亲指针,其效率要比使用二叉链表高一些。这里我使用的是二叉链表,虽然它效率很低(可能是是最差的),但站在学习的角度上看它不失为一种好方法:简洁和易于理解。
这样huffman树就与之前介绍的二叉表达式树很像了。首先给是结点定义:
 
class  HCNode {
public :
    
int  index;
    
int  weight;
    HCNode
*  left;
    HCNode
*  right;
    HCNode(
int  wgt,  int  n, HCNode *  lef  =  NULL, HCNode *  rgt  =  NULL) 
        : weight(wgt), index(n), left(lef), right(rgt) {}
};
 
这里假设是对26个英文字符进行编码。在构造huffman树时每个待编码字符在树中的位置会被打乱,需要一个整型量记录其在权值数组中的位序。
有了结点定义后下面该设法构造huffman树了,建树时需要一个一维整型数组(由用户给出),其中记录了每一个字符的权值。按照huffman的方法建树过程是这样的。
一、对给定的n个权值构成n棵二叉树的初始集合F={T1,T2,T3,...,Ti,...,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。
二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新构造的二叉树加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止,即为构造好的huffman树。
我们可以使用一个线性链表来存放每棵树的根结点指针。这样一来,每次从链表中摘下两个权值最小的结点,新建一个根结点,并指向它们,再把新建的结点指针插入链表尾部。反复这样进行直到链表内只剩一个结点时结束。
在选择链表时有点犹豫,刚开始用的是STL中的链表类<list>,但最后还是改成用我之前编的LinkList了。因为其中一些函数确实能让这个程序简单不少。还是先把代码贴上来:
 
#include  " ../../线性表(链式存储)/LinkList.h "
#include 
" ../../线性表(链式存储)/Node.h "
#include 
" ../../require.h "
// 省略若干...
class  HuffmanCode {
    LinkList
< HCNode *>  m_nodeList;
    
string *  m_ptrCode;
    
int  m_nNum;  // 编码的字符个数
    
// 生成huffman树
     void  createTree( int   * w,  int  n);
    
// 先序遍历huffman树
     void  preOrder(HCNode *  cur,  long  code  =   1 );
    
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值