南邮哈夫曼编码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,一经查实,立即删除!

相关文章

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

sci遴选目标是收录相对较为重要的期刊&#xff0c;但世界上期刊太多太多&#xff0c;从中遴选出重要的期刊&#xff0c;会有筛选标准&#xff0c;即sci只会收录部分期刊。众多期刊中&#xff0c;谁能成为sci期刊&#xff0c;就要达到sci收录的标准&#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…

【JZOJ4819】【NOIP2016提高A组模拟10.15】算循环

题目描述 输入 输出 样例输入 167 198 样例输出 906462341 数据范围 解法 令f(n)∑ni1i&#xff0c;g(n)∑ni1i2 易得ans∑ni1∑mj1f(n−i1)∗f(m−j1) 等价于ans∑ni1∑mj1f(i)∗f(j) 显然f(n)n∗(n−1)/2&#xff1b; 拆开得ans14∑ni1∑mj1i∗(i1)∗j∗(j1) 再得ans14∑…

《信息安全系统设计基础》 第五周学习总结

20145224 《信息安全系统设计基础》第五周学习总结 教材学习内容总结 3.1 历史观点 X86寻址的三个时代&#xff1a; 1、DOS时代的平坦模式&#xff0c;不区分用户空间和内核空间&#xff0c;很不安全&#xff1b; 2、8086的分段模式&#xff1b; 3、IA32的带保护模式的平坦模式…

Android内存优化(使用SparseArray和ArrayMap代替HashMap)

出处&#xff1a;Sunzxyong HashMap HashMap内部是使用一个默认容量为16的数组来存储数据的&#xff0c;而数组中每一个元素却又是一个链表的头结点&#xff0c;所以&#xff0c;更准确的来说&#xff0c;HashMap内部存储结构是使用哈希表的拉链结构&#xff08;数组链表&#…

VS2010与QT的集成开发环境

http://blog.csdn.net/hbsong75/article/details/9293773 QT与Java有点类似&#xff0c;也是一种跨平台的软件&#xff08;当然在windows平台和Linux平台需要安装相应的QT开发环境和运行库&#xff0c;类似于JAVA在不同平台下的虚拟机JVM环境&#xff09;&#xff0c;因此对于某…

各种排序笔记---基于非比较排序部分

在计算机科学中&#xff0c;排序是一门基础的算法技术&#xff0c;许多算法都要以此作为基础&#xff0c;不同的排序算法有着不同的时间开销和空间开销。排序算法有非常多种&#xff0c;如我们最常用的快速排序和堆排序等算法&#xff0c;这些算法需要对序列中的数据进行比较&a…

SharePoint Framework 构建你的第一个web部件(三)

&#xfeff;&#xfeff;博客地址&#xff1a;http://blog.csdn.net/FoxDave本篇接上一讲&#xff0c;我们一起来看一下如何部署和测试本地开发的web部件。在SharePoint中预览web部件SharePoint工作台在SharePoint中被承载&#xff0c;用来在开发环境预览和测试本地web部件。它…

mysql limit asc_MySql sql优化之order by desc/asc limit M-阿里云开发者社区

Order by desc/asc limit M是我在mysql sql优化中经常遇到的一种场景&#xff0c;其优化原理也非常的简单&#xff0c;就是利用索引的有序性&#xff0c;优化器沿着索引的顺序扫描&#xff0c;在扫描到符合条件的M行数据后&#xff0c;停止扫描&#xff1b;看起来非常的简单&am…

Spring 4 官方文档学习(十)数据访问之JDBC

说明&#xff1a;未修订版&#xff0c;阅读起来极度困难 1、Spring框架JDBC的介绍 Spring JDBC - who does what? 动作Spring你定义连接参数 是打开连接是 指定SQL语句 是声明参数&#xff0c;提供参数值 是准备、执行语句是 迭代结果&#xff08;如果有&#xff09;是 操作每…

协方差

协方差一般用来研究诸多实验中各个变量之间的关系。举个例子来说&#xff0c;有n个实验&#xff0c;每个实验得到两个数据&#xff0c;分别为变量x1和变量x2。设n 7。这七组实验得到的数据分别为&#xff1a; (3,5), (4,5.5),(2,4),(6,7),(8,10),(2,5),(5,7.5) 用MATLAB…

行为类模式(二):命令(Command)

定义 将一个请求封装成一个对象&#xff0c;从而让你使用不同的请求把客户端参数化&#xff0c;对请求排队或者记录请求日志&#xff0c;可以提供命令的撤销和恢复功能。 UML 优点 能比较容易的设计一个命令队列可以较容易的将命令加入日志允许接收请求的一方是否处理请求可以容…

win7 nginx mysql php_windows7配置Nginx+php+mysql的详细教程

最近在学习PHP&#xff0c;想把自己的学习经历记录下来&#xff0c;并写一些经验&#xff0c;仅供参考交流。此文适合那些刚刚接触PHP&#xff0c;想要学习并想要自己搭建NginxPHPMysqL环境的同学。当然&#xff0c;你也可以选择集成好的安装包&#xff0c;比如 wamp等&#xf…