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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

这样做的意义是什么呢?

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

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

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

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

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

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

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

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

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

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

  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值。

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

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

  1. 南邮计算机技术专业课,南邮考研辅导班-计算机技术(专业学位)考研科目_启道.pdf...

    南邮考研辅导班 - 计算机技术(专业学位)考研科目 _启道 启道考研分享 南京邮电大学, 简称 "南邮" (NJUPT),坐落于六朝古都南京, 是原由工业与信息化部直属, 现工业与 ...

  2. 单片机STC89C52_C语言代码_来回流水_软件延时

    单片机STC89C52_C语言代码_来回流水_软件延时 //11.0592MHz: //50=3.3ms;80=5.2ms;100=6.5ms;1000=65ms; //5000=325ms;7800 ...

  3. c语言判断字符串的编码,C语言中判断一个char*是不是utf8编码

    C语言中判断一个char*是不是utf8编码 里我修改了一下, 纯ASCII编码的字符串也返回true, 因为UTF8和ASCII兼容 实例代码: int utf8_check(const char* ...

  4. 3个月计算机考研,【图片】(重开)三个月考研成功的心路历程 -南邮计算机【南京邮电大学研究生院吧】_百度贴吧...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 这个帖子教你如何在较短时间拿到大部分的分值. 声明:这个经验是我个人的经历可能对有些人不适合 时间充裕的还是老老实实打基础,按部就班的三轮复习. 18年考 ...

  5. 在命令提示符输出c语言代码_您可以在Windows命令提示符中更改输出缓冲区的大小吗?...

    在命令提示符输出c语言代码 If you are someone who loves using the Windows Command Prompt, you may have found your ...

  6. 进制转换c语言代码_奇怪的C语言代码,有些函数在变量前加上(void)是什么类型转换?...

    C语言的语法极其简洁,即使是初次接触编程语言的初学者也能很快学完它的语法.不过,C语言也是一门"灵活得过了头"的编程语言,对于很多初学者来说,编写C语言程序就好像拿着一堆最基本的砖 ...

  7. 南邮-软件设计实验(C语言版)

    说在前面:所有代码实现均在VC++6.0环境下测试成功.建立文件(*.c),粘贴代码即可,运行~ 本次设计实验主要是对C语言字符串.数组.结构体.文件读写等知识的实际运用.文章中的代码测试基本可以运行 ...

  8. java 图片转成base64编码_java语言中如何将一个图片转换为base64编码的数据呢?

    摘要: 下文讲述java语言中将图片转换为base64编码的方法分享,如下所示: 例: /* file为图片文件对象 filePath为转换后base64的存储位置 */ public static ...

  9. 软件测试c语言代码_软件测试理论知多少?

    有源医疗器械很多都是带有软件的,今天一起了解下软件测试理论:软件测试目的,软件测试定义,软件测试原则,软件测试分类,软件测试方法,测试基本流程. 软件测试定义:软件测试(英语:software tes ...

最新文章

  1. 用ZooKeeper做为注册中心搭建基于Spring Cloud实现服务注册与发现
  2. UVa 11971 - Polygon(几何概型 + 问题转换)
  3. 干货 | 广电行业数字化时代的「数据破局」指南
  4. 互联网日报 | 6月7日 星期一 | 华为已捐献鸿蒙全部基础能力;芝麻信用7年免押金4000亿;奈雪的茶通过港交所上市聆讯...
  5. python读取us7ascii字符集Oracle数据库中文乱码问题的解决方案
  6. 纯惯导卡尔曼滤波器代码实例解读01
  7. phoenix的元数据一般存在哪里_Phoenix常用操作
  8. (转)ETL利器Kettle实战应用解析系列一【Kettle使用介绍】
  9. Android之编写测试用例
  10. [Tizen开发]SDB调试工具使用简介
  11. 一文了解11个常见的多变量分析方法!
  12. 深度学习Tir-Hi3559A使用unbuntu系统
  13. 网上打开与关闭gps几种方案测试,其中方案3可行
  14. 今天第一次做PIZZA,很成功.
  15. LayuiAdmin+TP5.1 数据表格添加数据详解
  16. 微信的商户单号和交易单号_教你获取支付宝和微信的订单号、商户单号,涨知识了...
  17. 图像滤镜艺术---乐高像素拼图特效滤镜的代码实现
  18. 苹果6怎样分屏_皮皮喵聚合搜索漫画APP苹果版IOS付费版更新 0.3.6,已购账号快来更新,分享一下,兑换码大赠送活动,共享喜悦!...
  19. 母牛2年生小牛 5年后并死去的算法
  20. 嵌入式软考备考_8 软件测试

热门文章

  1. 前端学习(1294):相对路径和绝对路径
  2. java学习(93):线程的创建方法二
  3. 树莓派使用STEP5:安装samba文件共享服务器
  4. js执行shell命令的几种方式(Node)
  5. python web自动化测试实验报告_Python:web自动化测试
  6. sql 查询优化小计
  7. 解决ubuntu16.04 qt5.9.1无法输入中文
  8. Dubbo xml配置 和注解配置 写法
  9. 《信息安全系统设计基础》 第五周学习总结
  10. windows cmd后ipconfig后提示不是内部命令或外部命令