一、题目描述

哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。其压缩率通常在20%~90%之间。哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。一个包含100,000个字符的文件,各字符出现频率不同,如下表所示:

有多种方式表示文件中的信息,若用0,1码表示字符的方法,即每个字符用唯一的一个0,1串表示。若采用定长编码表示,则需要3位表示一个字符,整个文件编码需要300,000位;若采用变长编码表示,给频率高的字符较短的编码;频率低的字符较长的编码,达到整体编码减少的目的,则整个文件编码需要(45×1+13×3+12×3+16×3+9×4+5×4)×1000=224,000位,由此可见,变长码比定长码方案好,总码长减小约25%。

二、举例

我们用一个简单的例子,来简单描述下哈夫曼编码是什么?有什么好处?

场景:X地区需要向Y地区发送一些文本,两地之间通过电缆(或者通过电报)连接,要求用最少的二进制流传递信息:ABACDAAB

可以看到该信息中一共出现4个A,2个B,C、D各1个

  • 如果用常见的二进制形式编码,那么A:00,B:01,C:10,D:11;信息转二进制流为:0001001011000001,一共是16位。
  • 如果用哈夫曼编码,则其的一种为A:1,B:01,C:000,D:001;信息转二进制流为:10110000011101,一共是14位,比普通的少2位。

这时,我们可能就明白了哈夫曼编码不就是频率出现越高,其编码越短吗?

我们尝试这样来赋值,A:0,B:1,C:01,D:10;二进制流为:0100110001,这个只有10位,比哈夫曼还少呢!

上面的说法似乎有道理,但是我们忽略了哈夫曼的用途可能是信息传输和压缩。当我们把编码规则和二进制流告诉接收方时,他们需要把这些二进制流还原为看得懂的信息。普通编码和哈夫曼编码都可以顺利还原,而第三种则不可能还原,0100…到底是ABAA…还是CAA…呢?

所以哈夫曼编码必须是前缀码,而且还是最优前缀码(前缀码定义:在一个字符集中,任何一个字符的编码都不是另一个字符编码的前缀)

因此,必须达到以上两点构造出的编码才是哈夫曼码。

三、算法设计与分析

1、前缀码:
对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀。这种编码称为前缀码。编码的前缀性质可以使译码方法非常简单;例如001011101可以唯一的分解为0,0,101,1101,因而其译码为aabe。
译码过程需要方便的取出编码的前缀,因此需要表示前缀码的合适的数据结构。为此,可以用二叉树作为前缀码的数据结构:树叶表示给定字符;从树根到树叶的路径当作该字符的前缀码;代码中每一位的0或1分别作为指示某节点到左儿子或右儿子的“路标”。
图-1a 与固定长度编码对应的树; 图-1b 对应于最优前缀编码的树

从上图可以看出,表示最优前缀码的二叉树总是一棵完全二叉树,即树中任意节点都有2个儿子。图a表示定长编码方案不是最优的,其编码的二叉树不是一棵完全二叉树。在一般情况下,若C是编码字符集,表示其最优前缀码的二叉树中恰有|C|个叶子。每个叶子对应于字符集中的一个字符,该二叉树有|C|-1个内部节点。

给定编码字符集C及频率分布f,即C中任一字符c以频率f©在数据文件中出现。C的一个前缀码编码方案对应于一棵二叉树T。字符c在树T中的深度记为dT©。dT©也是字符c的前缀码长。则平均码长定义为:

使平均码长达到最小的前缀码编码方案称为C的最优前缀码。

2、构造哈夫曼编码:
哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码。其构造步骤如下:

  • (1)哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。
  • (2)算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T
  • (3)假设编码字符集中每一字符c的频率是f©。以f为键值的优先队列Q用在贪心选择时有效地确定算法当前要合并的2棵具有最小频率的树。一旦2棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的2棵树的频率之和,并将新树插入优先队列Q。经过n-1次的合并后,优先队列中只剩下一棵树,即所要求的树T。

构造过程如图-2所示:
图-2 哈夫曼树构造过程

三、结果与分析

本实验以算法导论书中的例题为测试用例,来验证算法的正确性。即
实验结果截图如下图-7,结果与题目描述中给出的变长代码字一样:

图-7 实验结果截图

五、实验总结

1、实验结果与给出的变长代码字一样,算法正确,且哈夫曼编码问题是一个贪心算法问题。采用哈夫曼编码技术可以最小化总的编码长度,从而实现数据文件的压缩存储。
2、构造好哈夫曼树后,可用排列树回溯法来打印哈夫曼编码,即遇到左子树向左走,vector添加记录0;遇到右子树向右走,vector添加记录1;走到叶子节点并打印出叶节点的编码后回溯,同时往上退一层,则vector弹出一个值。如此不断回溯下去,即可打印所有字符编码。

六、源代码(C++)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;//Huffman树的节点类
typedef struct Node
{char value;               //结点的字符值int weight;               //结点字符出现的频度Node *lchild,*rchild;     //结点的左右孩子
}Node;//自定义排序规则,即以vector中node结点weight值升序排序
bool ComNode(Node *p,Node *q)
{return p->weight<q->weight;
}//构造Huffman树,返回根结点指针
Node* BuildHuffmanTree(vector<Node*> vctNode)
{while(vctNode.size()>1)                            //vctNode森林中树个数大于1时循环进行合并{sort(vctNode.begin(),vctNode.end(),ComNode);   //依频度高低对森林中的树进行升序排序Node *first=vctNode[0];    //取排完序后vctNode森林中频度最小的树根Node *second=vctNode[1];   //取排完序后vctNode森林中频度第二小的树根Node *merge=new Node;      //合并上面两个树merge->weight=first->weight+second->weight;merge->lchild=first;merge->rchild=second;vector<Node*>::iterator iter;iter=vctNode.erase(vctNode.begin(),vctNode.begin()+2);    //从vctNode森林中删除上诉频度最小的两个节点first和secondvctNode.push_back(merge);                                 //向vctNode森林中添加合并后的merge树}return vctNode[0];            //返回构造好的根节点
}//用回溯法来打印编码
void PrintHuffman(Node *node,vector<int> vctchar)
{if(node->lchild==NULL && node->rchild==NULL){//若走到叶子节点,则迭代打印vctchar中存的编码cout<<node->value<<": ";for(vector<int>::iterator iter=vctchar.begin();iter!=vctchar.end();iter++)cout<<*iter;cout<<endl;return;}else{vctchar.push_back(1);     //遇到左子树时给vctchar中加一个1PrintHuffman(node->lchild,vctchar);vctchar.pop_back();       //回溯,删除刚刚加进去的1vctchar.push_back(0);     //遇到左子树时给vctchar中加一个0PrintHuffman(node->rchild,vctchar);vctchar.pop_back();       //回溯,删除刚刚加进去的0}
}int main()
{cout<<"************ Huffman编码问题 ***************"<<endl;cout<<"请输入要编码的字符,并以空格隔开(个数任意):"<<endl;vector<Node*> vctNode;        //存放Node结点的vector容器vctNodechar ch;                      //临时存放控制台输入的字符while((ch=getchar())!='\n'){if(ch==' ')continue;      //遇到空格时跳过,即没输入一个字符空一格空格Node *temp=new Node;temp->value=ch;temp->lchild=temp->rchild = NULL;vctNode.push_back(temp);  //将新的节点插入到容器vctNode中}cout<<endl<<"请输入每个字符对应的频度,并以空格隔开:"<<endl;for(int i=0;i<vctNode.size();i++)cin>>vctNode[i]->weight;Node *root = BuildHuffmanTree(vctNode);   //构造Huffman树,将返回的树根赋给rootvector<int> vctchar;cout<<endl<<"对应的Huffman编码如下:"<<endl;PrintHuffman(root,vctchar);system("pause");
}

参考自:
allinallinallin

哈夫曼(Huffman)编码问题(C++)相关推荐

  1. huffman python,哈夫曼(Huffman)编码python代码实现

    首先看定义 哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种.Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率 ...

  2. 基于霍夫曼(Huffman)图像编码的图像压缩和重建-含Matlab代码

    目录 一.引言 二.霍夫曼Huffman编码 2.1 霍夫曼编码流程 2.2 输入数据的编码 三.霍夫曼解码 四.实验结果 五.参考文献 六.Matlab代码(GUI界面)获取 一.引言 随着通信与信 ...

  3. Huffman(哈夫曼)编码的C语言实现

    Huffman(哈夫曼)编码的C语言实现 本文将给出C语言的Huffman编码的原理,示例及C语言仿真结果,代码. 一.Huffman编码原理及举例 Huffman编码是一种信源编码,其编码目的在于以 ...

  4. 哈夫曼树和哈夫曼树编码

    在一般的数据结构的书中,树的那章后面,著者一般都会介绍一下哈夫曼(HUFFMAN) 树和哈夫曼编码.哈夫曼编码是哈夫曼树的一个应用.哈夫曼编码应用广泛,如 JPEG中就应用了哈夫曼编码. 首先介绍什么 ...

  5. labview 霍夫曼树_哈夫曼树编码实验报告_信息论与编码实验2 实验报告_信息论与编码报告...

    huffman编码C语言实验报告 今日推荐 180份文档 2014...4页 1下载券 安卓版100 doors 2攻略1... 3页 1下载券 <逃脱本色>doors....语文教育实习 ...

  6. 赫夫曼树编码的算法及应用习题--数据结构

    赫夫曼树编码的算法及应用习题 1.构造赫夫曼树的方法 1.根据给定的n个权值{w1,w2,---wn},构成n棵二叉树的集合F={T1,T2...,Tn},其中每棵二叉树中只有一个带权为Wi的根结点, ...

  7. java哈夫曼树编码_哈夫曼树的编码实验

    Java哈夫曼编码实验--哈夫曼树的建立,编码与解码 建树,造树,编码,解码 一.哈夫曼树编码介绍 1.哈夫曼树: (1)定义:假设有n个权值{w1, w2, ..., wn},试构造一棵含有n个叶子 ...

  8. 哈夫曼树的构建及哈夫曼树编码

    哈夫曼树的构建: 注意:(1).首先把一组数3 5 6 8 9 12 15从小到大排列 (2).选取里面最小2个,顶点出为2个数的和 (3).新产生的顶点在与原先的数字进行比较,在里面选取2个最小的数 ...

  9. 哈夫曼 (Huffman) 树的动画演示

     哈夫曼 (Huffman) 树的动画演示: http://people.cs.pitt.edu/~kirk/cs1501/animations/Huffman.html 此网站中亦有诸多其它算法 ...

  10. java 实现部门树_(java实现)哈夫曼(Huffman)树编码(自编压缩项目基础)

    哈夫曼树 给定 n 个权值作为 n 个叶子结点,构造一棵二叉树, 若该树的带权路径长度(wpl) 达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree), 也叫霍夫曼树. 哈 ...

最新文章

  1. 在 Java Web 项目中,Service 层和 Dao 层真的有必要每个类都加上接口吗
  2. Couchbase 2.0归类视图简介
  3. 安装 VMware Tools
  4. 如何查看使用 Cloud Toolkit 部署应用的实时日志
  5. PyCharm中Scrapy的安装
  6. BZOJ3638|CodeForces 280D k-Maximum Subsequence Sum
  7. 新iPhone销量将持续走低 因为旧iPhone够用好几年
  8. Linux 增加swap空间大小
  9. 7.企业安全建设入门(基于开源软件打造企业网络安全) --- 蜜罐与攻击欺骗
  10. PBOC规范研究之四、文件结构及访问(转)
  11. IE兼容性问题web.config设置
  12. 使用LordPE和Import REC脱壳
  13. 软件项目管理实验一补充
  14. oracle orderby多个字段,Oracle Order By用法详解
  15. 【前端就业课 第一阶段】HTML5 零基础到实战(二)超链接
  16. 以下选项属于python哲学内容的是_[南开大学]18秋学期(清考)《哲学与人生》在线作业...
  17. AMD提出的补丁使退出延迟降低21%左右
  18. STL库:vector
  19. 自然场景文字检测方案总结
  20. 车载测试-HIL硬件在环测试

热门文章

  1. Go开发报错 -- Golang strings.Builder type undefined
  2. C语言,利用递归调用函数求年龄问题 问题描述:有5个人坐在一起,问第5个人多少岁,他说比第4个人大2岁。问第4个人多少岁,他说比第3那个人大2岁。问第3个人多少岁,他说比第2个人大2岁。求5人年龄
  3. Java中gatSum方法是什么_Oracle中的SUM用法讲解
  4. Linxu:磁盘分区
  5. 几行代码实现谷歌百度搜索对比
  6. C语言实例——判断是否为闰年
  7. C#LeetCode刷题之#849-到最近的人的最大距离(Maximize Distance to Closest Person)
  8. java 学到什么实习_我的外展实习从今天开始! 到目前为止,这是我已经完成并学到的东西。...
  9. 一台服务器创建多个ssh_如何创建可用于生产的第一台安全服务器
  10. Pycharm使用秘籍