哈夫曼树(Huffman)

哈夫曼树

Huffman 编码问题

问题引入

什么是编码?

简单说就是建立【字符】到【数字】的对应关系,如下面大家熟知的 ASC II 编码表,例如,可以查表得知字符【a】对应的数字是十六进制数【0x61】

\000102030405060708090a0b0c0d0e0f
0000000102030405060708090a0b0c0d0e0f
0010101112131415161718191a1b1c1d1e1f
002020!"#$%&()*+,-./
00300123456789:;<=>?
0040@ABCDEFGHIJKLMNO
0050PQRSTUVWXYZ[\]^_
0060`abcdefghijklmno
0070pqrstuvwxyz{|}~7f

注:一些直接以十六进制数字标识的是那些不可打印字符

传输时的编码

  • java 中每个 char 对应的数字会占用固定长度 2 个字节
  • 如果在传输中仍采用上述规则,传递 abbccccccc 这 10 个字符
    • 实际的字节为 0061006200620063006300630063006300630063(16进制表示)
    • 总共 20 个字节,不经济

现在希望找到一种最节省字节的传输方式,怎么办?

假设传输的字符中只包含 a,b,c 这 3 个字符,有同学重新设计一张二进制编码表,见下图

  • 0 表示 a
  • 1 表示 b
  • 10 表示 c

现在还是传递 abbccccccc 这 10 个字符

  • 实际的字节为 01110101010101010 (二进制表示)
  • 总共需要 17 bits,也就是 2 个字节多一点,行不行?

不行,因为解码会出现问题,因为 10 会被错误的解码成 ba,而不是 c

  • 解码后结果为 abbbababababababa,是错误的

怎么解决?必须保证编码后的二进制数字,要能区分它们的前缀(prefix-free)

用满二叉树结构编码,可以确保前缀不重复

请添加图片描述

  • 向左走 0,向右走 1
  • 走到叶子字符,累计起来的 0 和 1 就是该字符的二进制编码

再来试一遍

  • a 的编码 0
  • b 的编码 10
  • c 的编码 11

现在还是传递 abbccccccc 这 10 个字符

  • 实际的字节为 0101011111111111111(二进制表示)
  • 总共需要 19 bits,也是 2 个字节多一点,并且解码没有问题了,行不行?

这回解码没问题了,但并非最少字节,因为 c 的出现频率高(7 次)a 的出现频率低(1 次),因此出现频率高的字符编码成短数字更经济

考察下面的树

请添加图片描述

  • 00 表示 a
  • 01 表示 b
  • 1 表示 c

现在还是传递 abbccccccc 这 10 个字符

  • 实际的字节为 000101 1111111 (二进制表示)
  • 总共需要 13 bits,这棵树就称之为 Huffman 树
  • 根据 Huffman 树对字符和数字进行编解码,就是 Huffman 编解码

Huffman 树

public class HuffmanTree {Node root;String code;private static class Node{char ch;int freq;String code;Node left;Node right;public Node(char ch) {this.ch = ch;}public Node(char ch, int freq) {this.ch = ch;this.freq = freq;}public Node(int freq, Node left, Node right) {this.freq = freq;this.left = left;this.right = right;}public boolean isLeaf(){return this.left == null && this.right == null;}}public HuffmanTree(String s){char[] charArray = s.toCharArray();Map<String,Integer> map = new HashMap();for (char c : charArray) {Integer i = map.getOrDefault(String.valueOf(c),0);map.put(String.valueOf(c),i+1);}PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(v -> v.freq));for (String string : map.keySet()) {Node node = new Node(string.charAt(0), map.get(string));queue.add(node);}while(queue.size() > 1){Node n1 = queue.poll();Node n2 = queue.poll();Node node = new Node(n1.freq + n2.freq, n1, n2);queue.add(node);}root = queue.peek();s = doEncode(root,new StringBuilder(),s);code = s;}
}

Huffman 编解码

补充两个方法,注意为了简单期间用了编解码都用字符串演示,实际应该按 bits 编解码

public class HuffmanTree {// ...// 编码private String doEncode(Node node,StringBuilder sb,String s){if(!node.isLeaf()){s = doEncode(node.left,sb.append(0),s);sb.deleteCharAt(sb.length()-1);s = doEncode(node.right,sb.append(1),s);sb.deleteCharAt(sb.length()-1);}else{node.code = sb.toString();while(s.contains(String.valueOf(node.ch))){s = s.replace(String.valueOf(node.ch), node.code);}}return s;}public String encode(){return code;}public String decode(String code){Node node = root;StringBuilder sb = new StringBuilder();char[] charArray = code.toCharArray();for (int i = 0; i < charArray.length; i++) {if(charArray[i] == '0'){node = node.left;}else {node = node.right;}if(node.isLeaf()){sb.append(node.ch);node = root;}}return sb.toString();}public static void main(String[] args) {HuffmanTree tree = new HuffmanTree("aabcccccc");String encode = tree.encode();System.out.println(encode);String decode = tree.decode(encode);System.out.println(decode);}
}

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

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

相关文章

php下curl发送cookie

目录 一&#xff1a;使用 CURLOPT_COOKIE 选项 二&#xff1a;CURLOPT_COOKIEFILE 三&#xff1a;CURLOPT_HTTPHEADER php curl发送cookie的几种方式,下面来介绍下 一&#xff1a;使用 CURLOPT_COOKIE 选项 通过设置 CURLOPT_COOKIE 选项&#xff0c;你可以将 cookie 字符…

用ChatGPT创作留学文书三阶玩法详解!

最近人工智能聊天工具Chagpt火爆全网&#xff0c;从闲聊到编程&#xff0c;文学到菜谱&#xff0c;似乎以一种无所不能的姿态推翻了早期“人工智障”的屈辱招牌&#xff0c;正逐步进入并改变我们生产生活方式。作为文字处理大师&#xff0c;如果让人工智能来执笔大学申请文书会…

Linux 下查看端口以及释放端口

目录 一、查看端口是否被占用 1、使用 netstat 命令 2、使用 lsof 命令 二、释放端口 1、使用kill命令 2、使用 fuser 命令 三、netstat 四、lsof 五、fuser 一、查看端口是否被占用 在 Linux 系统上&#xff0c;你可以使用 netstat 或 lsof 命令来查看端口是否被占用。…

串口WiFi模块简介、工作原理、工作方式等8大知识点

WiFi模块又名串口WiFi模块&#xff0c;属于物联网传输层&#xff0c;功能是将串口或TTL电平转为符合WiFi无线网络通信标准的嵌入式模块&#xff0c;内置无线网络协议IEEE802.11b.g.n协议栈以及TCP/IP协议栈。串口WIFI模块&#xff0c;体积小&#xff0c;功耗低&#xff0c;采用…

springboot124中药实验管理系统设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的中药实验管理系统设计与实现 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章…

CentOS 安装 Ruby

1.下载 Ruby3.3 并安装 依次执行 wget https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.0.tar.gz tar -zxvf ruby-3.3.0.tar.gz cd ruby-3.3.0 ./configure make make install 2.查看版本 ruby -v

深入了解Spring事件机制的作用与应用场景

Spring的事件机制为应用程序提供了一种灵活且解耦的方式来处理事件&#xff0c;它基于观察者模式构建&#xff0c;使得不同组件之间能够更加独立地工作。本文将深入探讨Spring事件机制的作用以及在实际应用中的使用场景。 作用&#xff1a; 解耦组件&#xff1a; 通过事件机制…

住宅IP VPS的應用指南

什麼是住宅IP VPS&#xff1f; 首先&#xff0c;我們來解釋一下什麼是住宅IP VPS。VPS是Virtual Private Server的縮寫&#xff0c;中文名為虛擬專用伺服器。它是一種虛擬化技術&#xff0c;可以將一臺物理伺服器分割為多個虛擬伺服器&#xff0c;每個虛擬伺服器都有自己獨立的…

【算法Hot100系列】合并区间

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

跨界做电商:百度、抖音、小红书异途同归

早些年&#xff0c;国内电商市场一直被淘宝、京东牢牢把持&#xff0c;其他电商平台只能跟在后面分一点羹。后来&#xff0c;随着拼多多崛起&#xff0c;电商格局开始生变。尤其是近两年&#xff0c;抖音、小红书、百度等玩家纷纷入局&#xff0c;更是让刚刚平静的电商市场又迎…

git tag的用法详解

目录 一、tag标识一个commit 二、查看tag 三、对分支打tag 四、删除tag 五、根据某个tag来clone 一、tag标识一个commit tag是用于去标记一个特定的commit。通常&#xff0c;在进行编译部署之前&#xff0c;我们需要对某一个即将release的版本进行tag&#xff0c;例如tag为…

vue常用指令(v-if)

一、v-if 指令 作用: 根据表达值的真假,切换元素的显示和隐藏( 操纵dom 元素 ) 二、代码演示 1、v-if 设置条件&#xff0c;添加按键修改状态 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta na…

运用ETLCloud快速实现数据清洗、转换

一、数据清洗和转换的重要性及传统方式的痛点 1.数据清洗的重要性 数据清洗、转换作为数据ETL流程中的转换步骤&#xff0c;是指在数据收集、处理、存储和使用的整个过程中&#xff0c;对数据进行检查、处理和修复的过程&#xff0c;是数据分析中必不可少的环节&#xff0c;对…

基本处理函数(ProcessFunction)

基本处理函数 处理函数在数据流处理中扮演着核心角色&#xff0c;负责定义数据流的转换操作。在Flink中&#xff0c;处理函数作为一种特殊的转换算子&#xff0c;提供了强大的功能来处理数据流。Flink几乎所有的转换算子都提供了对应的函数类接口&#xff0c;处理函数也不例外。…

C/C++ - Auto Reference

目录 auto Reference auto 当使用auto​​关键字声明变量时&#xff0c;C编译器会根据变量的初始化表达式推断出变量的类型。 自动类型推断&#xff1a;auto​​关键字用于自动推断变量的类型&#xff0c;使得变量的类型可以根据初始化表达式进行推导。 初始化表达式&#x…

阿里云对象存储(OSS)服务

阿里云对象存储&#xff08;OSS&#xff09;服务 引入依赖 <!--阿里云OSS服务--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>aliyun-oss-spring-boot-starter</artifactId><exclusions><!--排除默认版本的依…

初学数据结构:二叉树相关oj题

目录 1. 相同的树2. 另一棵树的子树3. 翻转二叉树4. 平衡二叉树5. 对称二叉树6. 二叉树构建与遍历7. 二叉树的层序遍历8. 二叉树的最近公共祖先9. 从前序与中序遍历序列构造二叉树10. 从中序与后序遍历序列构造二叉树11. 根据二叉树创建字符串12. 二叉树的前序遍历非递归实现13…

ORACLE数据导出工具

最近应公司要求导出数据为csv格式文件供业务人员存档查看&#xff0c;同时还需要按照指定分隔符导出其他文本格式&#xff0c;待数据迁移使用&#xff0c;就是根据指定的sql&#xff0c;按照指定的分隔符和文本格式导出数据。所使用的数据是oralce&#xff0c;由于生产环境又没…

openssl3.2/test/certs - 037 - 768-bit leaf key

文章目录 openssl3.2/test/certs - 037 - 768-bit leaf key概述笔记END openssl3.2/test/certs - 037 - 768-bit leaf key 概述 openssl3.2 - 官方demo学习 - test - certs 笔记 /*! * \file D:\my_dev\my_local_git_prj\study\openSSL\test_certs\037\my_openssl_linux_do…

树莓派3b使用selenium并更换自带的chromium浏览器版本

树莓派3b自带python3.7&#xff0c;先安装selenium pip3 install selenium3.3 卸载自带的chromium sudo apt --fix-broken install sudo apt-get purge chromium-browser sudo apt-get remove chromium-browser下载chromium 进入网址&#xff1a;http://ports.ubuntu.com/p…