哈夫曼编码的实现

哈夫曼编码

在说哈夫曼编码之前,先要讲清楚编码集是什么东西。相信写过代码的人,一定听说过ASCII、 UTF-8、GBK 这些编码集。这些编码集合,本质上都是一个二进制和字符之间映射关系,拿最简单的 ASCII 来说吧,使用 0x30 代表字符 0 ,0x31 代表字符 1,当打印设备看到 0x30 后,就打印出字符 0 了。ASCII 最初是由美国弄出来,他们使用的英文,所以只考虑了英文中的字符,那还有很多字符,比如,阿拉伯文字、俄文这些文字怎么表示,于是就有了 UTF-8 这些。

讲了半天,哈夫曼编码和字符集有什么关系呢?大家都知道在计算机刚诞生的时候,其内存是非常小的,所以希望能设计出一个字符集,这个字符集提高单位内存保存数据信息量。以 ASCII 码为例,它使用 1 个字节保存来保存,如果一封 e-mail 有 150 个字符,则需要 150 个字节来存储。那么有没有办法减少一些呢?大家仔细观察,英文的字符,有的使用比较多,有的比较少,能不能使用比较短的比特为表示比较常用的字符呢?比如,a 比 z 使用的比较多,我们可以使用两个比特位来表示,z 可以使用三个表示。这样的话,在越长的文本中,使用这种方式保存数据,是不是越能节省内存。

哈夫曼编码就是生成这样自定义字符集的算法。

上代码

哈弗树:

    /**输入一个字符串,计算出各个字符出现的次数,出现的次数作为 Node 中的权重。然后在将 Node 放入到小根堆中。*/public static PriorityQueue<Node> getQueue(String input){Map<Character , Integer> cha = new HashMap<>();for (char c : input.toCharArray()) {cha.computeIfPresent(c , (key , oldValue)->{return oldValue + 1;});cha.computeIfAbsent(c , x -> {return 1;});}PriorityQueue<Node> nodes = new PriorityQueue<>(cha.size());cha.forEach((key , value) -> {nodes.add(new Node(key , null , null , value));});return nodes ;}/**根据小根堆,生成 Huffman 树。*/public static Node huffmanTree(PriorityQueue<Node> queue){Node head = null ;Node first = null ;Node second = null ;Node h =  null ;while(!queue.isEmpty()){first = queue.poll();second = queue.poll();if(null == second){break ;}h = new Node(null , first , second , first.weight + second.weight);head = h ;queue.add(h);}return head ;}public static class Node implements Comparable<Node>{public Node left ;public Node right ;public int weight ;public Character c ;public Node(Character c , Node l , Node r , int w){left = l ;right = r ;weight = w ;this.c = c ;}@Overridepublic int compareTo(Node o) {return this.weight - o.weight;}}   /**返回字符对应的 Huffman 编码*/public static Map<Character , String> huffmanCode(Node head){Map<Character , String> rs = new HashMap<>();maxLevel = process2(head, "", 0, rs);return rs ;}/**递归的找到 Huffman 编码*/public static int process2(Node head , String binaryStr , int value , Map<Character , String> map){if(head.c != null){map.put(head.c ,  binaryStr + value);int p = 1 + binaryStr.length();return p;}int p1 = process2(head.left ,  binaryStr + "1" , 1 , map);int p2 = process2(head.right ,  binaryStr + "0" , 0 , map);return Math.max(p1 , p2);}    

转码码和解码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.Iterator;
import java.util.Map;
import java.util.BitSet;
public class HuffmanEncoder {private static Logger logger = LoggerFactory.getLogger(HuffmanEncoder.class);/**使用 BitSet 保存 Huffman 编码*/public static Info encode(String input , Map<Character , String> mapping){BitSet bs = new BitSet();int idx = 0 ;for(char c : input.toCharArray()){String code = mapping.get(c);char[] charArray = code.toCharArray();for(int i = charArray.length - 1 ; i >= 0 ; i-- ){bs.set(idx++ , charArray[i] == '1');}}Iterator<Map.Entry<Character, String>> iterator = mapping.entrySet().iterator();// max 的作用是保存 Huffman 编码的最大长度,使用 max + 1 长度的连续为 1 的比特位作为结尾int max = 0 ;while (iterator.hasNext()) {Map.Entry<Character, String> next = iterator.next();if(max < next.getValue().length()){max = next.getValue().length();}}// 设置结尾字符for (int i = 0; i < max+1; i++) {bs.set(idx++ , true);}return new Info(max+1 , bs) ;}// 返回的信息体,max 是结束比特位的长度。// BitSet 是字符串转码后的比特数据public static class Info{public int max ;public BitSet bs ;public Info(int m , BitSet b){max = m ;bs = b ;}}
}

解码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.BitSet;
import java.util.Map;public class HuffmanDecoder {private static Logger logger = LoggerFactory.getLogger(HuffmanDecoder.class);public static String decode(HuffmanEncoder.Info info, Map<Integer, Character> mapping){StringBuilder sb = new StringBuilder();int sum = 0 ;int i = 0 ;int j = 0 ;int end = 0 ;for (int k = 0; k < info.max; k++) {end += (1 << k);}while(i < info.bs.length()) {sum += (info.bs.get(i)? 1<<j : 0);j++;// j 表示了当前比特的位数,当 >= max -1 的时候,才能解码// 避免混淆,例如,111 和 011 ,当读完 11 后,第三个 1 没读取,此时转化为 10 进制为 3 ,正好和 011 相等,// 这种情况就出现问题,所以一定要判断一下是否大于 info.max -1 if(mapping.containsKey(sum) && j >= info.max-1){sb.append(mapping.get(sum));sum = 0 ;j = 0 ;}// 判断是否等于结束字符。if(sum == end){break ;}i++;}return sb.toString();}
}

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

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

相关文章

温度对射频电路性能的影响

对于射频电路,通常会有使用温度范围的要求,即在特定的温度范围内其性能变化不超出指标要求的值。对于工业级产品,一般要求使用温度范围为-40℃~+70℃,而军品要求使用温度范围为-55℃~+85℃。有一些其他特殊使用场景的产品会有不同的要求。 不同的温度对电路性能的影响,…

Shell学习 - 2.27 Linux bc命令:一款数学计算器

Bash Shell 内置了对整数运算的支持&#xff0c;但是并不支持浮点运算&#xff0c;而 Linux bc 命令可以很方便的进行浮点运算&#xff0c;当然整数运算也不再话下。 bc是"Basic Calculator"的缩写。 bc 甚至可以称得上是一种编程语言了&#xff0c;它支持变量、数组…

软件设计:UML 模型图总结

1. 相关链接 参考教程&#xff1a; https://sparxsystems.com/resources/tutorials/ https://sparxsystems.com/enterprise_architect_user_guide/15.2/model_domains/whatisuml.html Unified Modeling Language (UML) description, UML diagram examples, tutorials and r…

单片机学习笔记——LED点阵

代码如下&#xff0c;注意管脚和扫描所用的hc595_write_data函数 #include "reg51.h"typedef unsigned int u16; //对系统默认数据类型进行重定义 typedef unsigned char u8;//定义74HC595控制管脚 sbit SRCLKP3^6; //移位寄存器时钟输入 sbit RCLKP3^5; //存储寄存…

Element-UI 下拉框单选转多选回显不清空绑定的值

需求 根据radio切换来更改下拉框是否多选 原因 单选和多选这两个 input 看上去没差别&#xff08;自身和层级都一致&#xff09;&#xff0c;vue出于提高性能&#xff0c;所以 vue 给复用了 解决方案 <template><section><el-radio-group v-model"radi…

【迅为iMX6Q】开发板 Linux version 6.6.3 SD卡 启动

开发环境 win10 64位 VMware Workstation Pro 16 ubuntu 20.04 【迅为imx6q】开发板&#xff0c; 2G DDR RAM linux-imx 下载 使用 NXP 官方提供的 linux-imx&#xff0c;代码地址为&#xff1a; https://github.com/nxp-imx/linux-imx 使用 git 下载 linux-imx&#xff…

Python 物联网入门指南(八)

原文&#xff1a;zh.annas-archive.org/md5/4fe4273add75ed738e70f3d05e428b06 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第三十章&#xff1a;制作机械臂 最后&#xff0c;我们终于到达了大多数人自本书开始以来就想要到达的地方。制作一个机械臂&#xff01;在…

中科国声携新品亮相北京InfoComm China 2024展

4月17日&#xff0c;北京InfoComm China 2024展&#xff08;北京专业视听技术和集成体验解决方案展览会&#xff09;在北京的国家会议中心盛大开幕。展会为期三天。作为备受瞩目的”会议系统国家队“&#xff0c;中科国声携众多优质会议音频产品及全新会议系统解决方案精彩亮相…

中缀表达式求值

题目 请写一个整数计算器&#xff0c;支持加减乘三种运算和括号。 示例1 输入&#xff1a;“12” 返回值&#xff1a;3示例2 输入&#xff1a;“(2*(3-4))*5” 返回值&#xff1a;-10示例3 输入&#xff1a;“32*3*4-1” 返回值&#xff1a;26 思路 经典的中缀表达式求值。…

“面包板”是什么?有啥用?

同学们大家好&#xff0c;今天我们继续学习杨欣的《电子设计从零开始》&#xff0c;这本书从基本原理出发&#xff0c;知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例&#xff0c;可以说是全面系统地介绍了电子设计所需的知识…

Vue3从入门到实战:深度掌握组件通信(上部曲)

props的概念&#xff1a; 当你使用Vue 3的组合式API时&#xff0c;props就是一种让你可以从父组件向子组件传递数据的方式。你可以想象成你在给子组件写一封信&#xff0c;把需要传递的信息放在信封里。 在Vue 3中&#xff0c;你可以在子组件的代码中定义props&#xff0c;就…

Dryad Girl Fawnia

一个可爱的Dryad Girl Fawnia的三维模型。她有ARKit混合形状,人形装备,多种颜色可供选择。她将是一个完美的角色,幻想或装扮游戏。 🔥 Dryad Girl | Fawnia 一个可爱的Dryad Girl Fawnia的三维模型。她有ARKit混合形状,人形装备,多种颜色可供选择。她将是一个完美的角色…

Web 题记

[极客大挑战 2019]LoveSQL 看到这种就肯定先想到万能密码&#xff0c;试试&#xff0c;得到了用户名和密码 总结了一些万能密码&#xff1a; or 11 oror admin admin-- admin or 44-- admin or 11-- admin888 "or "a""a admin or 22# a having 11# a havin…

Unity引擎Shader 技术点解释

Shader 类 定义&#xff1a; Shader 类是 Unity 中用于封装着色器程序的实例。它允许开发者在同一个文件中定义多个着色器程序&#xff0c;并指导 Unity 如何使用它们。 兼容性&#xff1a; Shader 类需要与 Unity 的渲染管线兼容。Unity 提供了两种主要的渲染管线&#xf…

C++ 之 newmat 矩阵运算库使用笔记

文章目录 Part.I IntroductionChap.I newmat 简介 Part.II 安装与编译Chap.I 直接使用源码Chap.II 基于 CMake 使用源码Chap.III 编译成库 Part.III 关于矩阵的构造与运算Chap.I 矩阵的构造与初始化Chap.II 矩阵的运算Chap.III 矩阵维数和类型的更改Chap.IV 矩阵最值统计 Refer…

【避坑/个人总结】CARLA仿真遇到问题——AttributeError: module “numpy“ has no attribute “bool“

问题描述 执行以下命令时&#xff1a; ./CarlaUE4.sh -prefernvidia // 以及 ros2 launch carla_shenlan_bridge_ego_vis carla_bridge_ego_vehilce.launch.py 出现以下的问题&#xff1a; 其中的报错类型及具体为&#xff1a;AttributeError: module "numpy" has…

线下商家地图标注服务/店铺地图定位/商铺地图标注服务源码

简介&#xff1a; 前台 单店标注信息提交&#xff0c; 连锁店标注信息提交 &#xff0c;支付订单查询&#xff0c;用户问题反馈 后台 系统基本设置&#xff0c;反馈信息查看&#xff0c;订单信息管理&#xff0c;地图信息管理&#xff0c;管理员密码设置&#xff0c;集成微信…

香港科技大学广州|数据科学与分析学域硕博招生宣讲会—华东师范大学专场

时间&#xff1a;2024年4月25日&#xff08;星期四&#xff09;13:30 地点&#xff1a;华东师范大学普陀校区文附楼507 报名链接&#xff1a;https://www.wjx.top/vm/Q0cKTUI.aspx# 跨学科研究领域 *数据驱动的人工智能和机器学习 *统计学习和建模 工业和商业分析 *特定行业…

鲲鹏920RDMA应用示例代码

当前针对鲲鹏920服务器&#xff0c;编写了RDMA通信传输代码&#xff0c;首先采用TCP socket进行管理信息获取&#xff0c;然后调用verbs函数接口进行数据传输。需要安装rdma-core-devel库才可以编译 代码如下&#xff1a; /** rdma_lib.h** Created on: 2021年1月1日* A…

在Linux系统中设定延迟任务

一、在系统中设定延迟任务要求如下&#xff1a; 要求&#xff1a; 在系统中建立easylee用户&#xff0c;设定其密码为easylee 延迟任务由root用户建立 要求在5小时后备份系统中的用户信息文件到/backup中 确保延迟任务是使用非交互模式建立 确保系统中只有root用户和easylee用户…