南邮哈夫曼编码c语言代码_漫画:“哈夫曼编码” 是什么鬼?

​在上一期,我们介绍了一种特殊的数据结构 “哈夫曼树”,也被称为最优二叉树。没看过的小伙伴可以点击下方链接:

漫画:什么是 “哈夫曼树” ?

那么,这种数据结构究竟有什么用呢?我们今天就来揭晓答案。

50c82804f60f5992b2c20e64d4fa438d.png

f822052a4627825584ca36f77eb8a07d.png

3daa2a7a7208ac7921c781e4d68159b7.png

4c5a89f088a550e8156e4f5627f76a2e.png

计算机系统是如何存储信息的呢?

计算机不是人,它不认识中文和英文,更不认识图片和视频,它唯一“认识”的就是0(低电平)和1(高电平)。

因此,我们在计算机上看到的一切文字、图像、音频、视频,底层都是用二进制来存储和传输的。

0b146864d24dca8936c0ceb834c3a95c.png

从狭义上来讲,把人类能看懂的各种信息,转换成计算机能够识别的二进制形式,被称为编码

编码的方式可以有很多种,我们大家最熟悉的编码方式就属ASCII码了。

在ASCII码当中,把每一个字符表示成特定的8位二进制数,比如:

70534facc8e4d5db3e183c67241756e8.png

显然,ASCII码是一种等长编码,也就是任何字符的编码长度都相等。

e0927df5896bb8c178cbd0d2723a7b4f.png

e30535740c82f9da002e0281049c85e8.png

718961de9ce389db2d29f77e39e87ea4.png

125dd608d04bdc52b2081a1a2e9e3edf.png

为什么这么说呢?让我们来看一个例子:

假如一段信息当中,只有A,B,C,D,E,F这6个字符,如果使用等长编码,我们可以把每一个字符都设计成长度为3的二进制编码:

1fcee6f6f03bbf5d77d54587d06e55c6.png

如此一来,给定一段信息 “ABEFCDAED”,就可以编码成二进制的 “000 001 100 101 010 011 000 100 011”,编码总长度是27。

9008fffcb68af28bf2fccbcc68390b7a.png

但是,这样的编码方式是最优的设计吗?如果我们让不同的字符对应不同长度的编码,结果会怎样呢?比如:

5b6a8fe5731b901ecec989f6d2459c61.png

如此一来,给定的信息 “ABEFCDAED”,就可以编码成二进制的 “0 00 10 11 01 1 0 10 1”,编码的总长度只有14。

12050542b055066b1a335999a6e62d9b.png

4639be287315bfe88d2395ad87c4abf0.png

918ee609841d197749aa91992d3ba5fa.png

a682b4eed81cb078c470b3d7680a515e.png

哈夫曼编码(Huffman Coding),同样是由麻省理工学院的哈夫曼博所发明,这种编码方式实现了两个重要目标:

1.任何一个字符编码,都不是其他字符编码的前缀。

2.信息编码的总长度最小。

b03b2c9134d946f3fc035b17410b3961.png

8b7c700d95f781b71d15f3976ce4ecb6.png

c2c4b08837bc9d0c1f18ab582a864ebb.png

哈夫曼编码的生成过程是什么样子呢?让我们看看下面的例子:

假如一段信息里只有A,B,C,D,E,F这6个字符,他们出现的次数依次是2次,3次,7次,9次,18次,25次,如何设计对应的编码呢?

我们不妨把这6个字符当做6个叶子结点,把字符出现次数当做结点的权重,以此来生成一颗哈夫曼树:

3a1fc8cf7b166a31940e352b9a3faffa.png

这样做的意义是什么呢?

哈夫曼树的每一个结点包括左、右两个分支,二进制的每一位有0、1两种状态,我们可以把这两者对应起来,结点的左分支当做0,结点的右分支当做1,会产生什么样的结果?

2af0c4f702d8d404ca98756c0012b0f6.png

这样一来,从哈夫曼树的根结点到每一个叶子结点的路径,都可以等价为一段二进制编码:

979b1959daaedac289f7770c61f6941a.png

上述过程借助哈夫曼树所生成的二进制编码,就是哈夫曼编码

现在,我们面临两个关键的问题:

首先,这样生成的编码有没有前缀问题带来的歧义呢?答案是没有歧义。

因为每一个字符对应的都是哈夫曼树的叶子结点,从根结点到这些叶子结点的路径并没有包含关系,最终得到的二进制编码自然也不会是彼此的前缀。

其次,这样生成的编码能保证总长度最小吗?答案是可以保证。

哈夫曼树的重要特性,就是所有叶子结点的(权重 X 路径长度)之和最小。

放在信息编码的场景下,叶子结点的权重对应字符出现的频次,结点的路径长度对应字符的编码长度。

所有字符的(频次 X 编码长度)之和最小,自然就说明总的编码长度最小。

84972dab0e1bfb180cca1323ad7784b3.png

021e328820b111855d26f5c0b9250869.png

d5ccf931f718accb1a7e2ebe08920364.png

a9d5fc0dc313f0a44b917865cfb325ae.png

0ebe03cf3b6268d1269388e7155ddb5a.png

cea76b0ed82b1a0e162c8e425ef71ab0.png

da4c73fef4b68add7a90f93046fe24fa.png

eef8329d0c47908cbae2b5ed52582c40.png
  1. private Node root;
  2. private Node[] nodes;
  3. //构建哈夫曼树
  4. public void createHuffmanTree(int[] weights) {
  5. //优先队列,用于辅助构建哈夫曼树
  6. Queue<Node> nodeQueue = new PriorityQueue<>();
  7. nodes = new Node[weights.length];
  8. //构建森林,初始化nodes数组
  9. for(int i=0; i<weights.length; i++){
  10. nodes[i] = new Node(weights[i]);
  11. nodeQueue.add(nodes[i]);
  12. }
  13. //主循环,当结点队列只剩一个结点时结束
  14. while (nodeQueue.size() > 1) {
  15. //从结点队列选择权值最小的两个结点
  16. Node left = nodeQueue.poll();
  17. Node right = nodeQueue.poll();
  18. //创建新结点作为两结点的父节点
  19. Node parent = new Node(left.weight + right.weight, left, right);
  20. nodeQueue.add(parent);
  21. }
  22. root = nodeQueue.poll();
  23. }
  24. //输入字符下表,输出对应的哈夫曼编码
  25. public String convertHuffmanCode(int index) {
  26. return nodes[index].code;
  27. }
  28. //用递归的方式,填充各个结点的二进制编码
  29. public void encode(Node node, String code){
  30. if(node == null){
  31. return;
  32. }
  33. node.code = code;
  34. encode(node.lChild, node.code+"0");
  35. encode(node.rChild, node.code+"1");
  36. }
  37. public static class Node implements Comparable<Node>{
  38. int weight;
  39. //结点对应的二进制编码
  40. String code;
  41. Node lChild;
  42. Node rChild;
  43. public Node(int weight) {
  44. this.weight = weight;
  45. }
  46. public Node(int weight, Node lChild, Node rChild) {
  47. this.weight = weight;
  48. this.lChild = lChild;
  49. this.rChild = rChild;
  50. }
  51. @Override
  52. public int compareTo(Node o) {
  53. return new Integer(this.weight).compareTo(new Integer(o.weight));
  54. }
  55. }
  56. public static void main(String[] args) {
  57. char[] chars = {'A','B','C','D','E','F'};
  58. int[] weights = {2,3,7,9,18,25};
  59. HuffmanCode huffmanCode = new HuffmanCode();
  60. huffmanCode.createHuffmanTree(weights);
  61. huffmanCode.encode(huffmanCode.root, "");
  62. for(int i=0; i<chars.length; i++){
  63. System.out.println(chars[i] +":" + huffmanCode.convertHuffmanCode(i));
  64. }
  65. }

这段代码中,Node类增加了一个新字段code,用于记录结点所对应的二进制编码。

当哈夫曼树构建之后,就可以通过递归的方式,从根结点向下,填充每一个结点的code值。

be2618829470056843472f165495cd33.png

c0c32be89c0e926f3c3a7f9698260d7f.png

在公众号后台回复“学习视频”,可以获得海量免费的IT视频课程哦~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/417948.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python安装lxml,在windows环境下

http://blog.csdn.net/g1apassz/article/details/46574963 文件下载&#xff1a;http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 找到对应的文件&#xff0c;例如&#xff1a;lxml-3.6.4-cp27-cp27m-win32.whl转载于:https://www.cnblogs.com/hooca/p/5952710.html

虚拟内存

虚拟内存用硬盘空间做内存来弥补计算机RAM空间的缺乏。当实际RAM满时&#xff08;实际上&#xff0c;在RAM满之前&#xff09; &#xff0c;虚拟内存就在硬盘上创建了。当物理内存用完后&#xff0c;虚拟内存管理器选择最近没有用过的&#xff0c;低优先级的 内存部分写到交换文…

查重多少合格_期刊论文查重一般多少合格?

sci遴选目标是收录相对较为重要的期刊&#xff0c;但世界上期刊太多太多&#xff0c;从中遴选出重要的期刊&#xff0c;会有筛选标准&#xff0c;即sci只会收录部分期刊。众多期刊中&#xff0c;谁能成为sci期刊&#xff0c;就要达到sci收录的标准&#xff0c;而标准少不了参考…

RegExp类型exec()方法的返回值说明

之前看别人写的插件经常会用到RegExp对象来验证输入&#xff0c;并且获取一些那时我并不晓得是什么东西的数组&#xff0c;来取值进行自定义的逻辑处理。今天学习了一下RegExp类型。终于有了一个初步的了解&#xff0c;至少不会看一会就感觉蒙圈了&#xff0c;记录分享一下 ECM…

记事本和textarea中的换行

换行符:chr(13)&chr(10) php替换$notestr_replace(chr(13).chr(10),"<br>",$note);

mysql-bin_MySQL - binlog日志简介及设置

基本概念binlog是Mysql sever层维护的一种二进制日志&#xff0c;与innodb引擎中的redo/undo log是完全不同的日志&#xff1b;其主要是用来记录对mysql数据更新或潜在发生更新的SQL语句&#xff0c;记录了所有的DDL和DML(除了数据查询语句)语句&#xff0c;并以事务的形式保存…

关于webservice(CXF)的一些理解

CXF是apache下开源的webservice框架&#xff0c;CXF依赖spring进行集成&#xff0c;支持soap1.1&#xff0c;soap1.2,XML/HTTP,RESTful HTTP 或者CORBA; 使用jaxws来实现 SOAP1.1,1.2; 1.通过编程的方法来实现CXF发布使用接口 &#xff1b; A. 使用了jaxwsSeverFactoryBean 这…

vb 获取汉字拼音的首字母

vb 获取汉字拼音的首字母 Public Function py(mystr As String) As String i Asc(mystr) Select Case i Case -20319 To -20284: py "A" Case -20283 To -19776: py "B" Case -19775 To -19219: py "C" Case -19218 To -18711: py "D&…

mysql 使用索引_mysql使用索引的注意事项

使用索引的注意事项使用索引时&#xff0c;有以下一些技巧和注意事项&#xff1a;1.索引不会包含有NULL值的列只要列中包含有NULL值都将不会被包含在索引中&#xff0c;复合索引中只要有一列含有NULL值&#xff0c;那么这一列对于此复合索引就是无效的。所以我们在数据库设计时…

IIS问题

问题1&#xff1a;未启用父路径 症状举例&#xff1a; Server.MapPath() 错误 ASP 0175 : 80004005 不允许的 Path 字符 /0709/dqyllhsub/news/OpenDatabase.asp&#xff0c;行 4 在 MapPath 的 Path 参数中不允许字符 ..。 原因分析&#xff1a; 许多Web页面里要用到诸…

shape的简单用法

shap节点-----------------------------------定义shape的值&#xff0c;必须是下面的之一&#xff1a;"rectangle" 矩阵&#xff0c;这也是默认的shape"oval"    椭圆"line"     一条水平的直线。这种shape必须使用 <stroke> 元素…

mysql 存储过程 set into_mysql存储过程之创建(CREATE PROCEDURE)和调用(CALL)及变量创建(DECLARE)和赋值(SET)操作方法...

本文实例讲述了mysql存储过程之创建(CREATE PROCEDURE)和调用(CALL)及变量创建(DECLARE)和赋值(SET)操作方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;存储过程创建(CREATE PROCEDURE)和调用(CALL)这个咱就不说什么定义啦术语啦啥的&#xff0c;直接看着实例来慢…

bzoj2060[USACO2010,Nov]Visiting Cows拜访奶牛

题目链接&#xff1a;bzoj上是权限题~ 题目大意&#xff1a; 题解&#xff1a; 树型DP f[i][0]表示不选i&#xff0c;f[i][1]表示选i。(因为很水..就不详写了) 可列出方程&#xff1a; f[i][0]sigama(max(f[y][1],f[y][0])); f[i][1]sigama(f[y][0]); y为i的孩子 #include<c…

SQL中及Access的空值

在SQL中及Access有空值这个类型,不能直接用""去比较,即使是打开数据库要用以下字符串 select from * where filed is null 打开之后也必须有isnull(rs(" "))来判断.它的值是否为空. 所以只要涉及到数据为空的情况都应用isnull(rs(" "))来返回tru…

mysql修改engine_mysql下修改engine引擎的方法

修改my.ini&#xff0c;在[mysqld]下加上default-storage-engineINNODB其中红色字体部分是要指定的引擎名称。用sql语句修改已经建成表的引擎&#xff1a;alter table tableName typeInnoDB下面贴出我的my.ini文件供参考(http://www.my400800.cn)&#xff1a;[mysqld]basedirC:…