使用HashMap实现,对一个字符集进行哈夫曼编码

最终达到的效果:

调用一个类

class HuffmanCodin{.....}

使用类中的静态方法,获取哈夫曼编码:


事前准备——哈夫曼树的节点定义

class Node implements Comparable<Node> {int weight;//权重Node left;Node right;char ch;//关键字,字符String code;//相应的哈夫曼编码public Node(char ch, int weight) {//构造方法,键值对this.weight = weight;this.ch = ch;}//构造方法,只设置出现频率public Node(int weight) {this.weight = weight;}//重写compareTo方法@Overridepublic int compareTo(Node node) {if(this.weight - node.weight==0){//如果两个字符出现的频率一样,那么就比较字典序(两个字符一定是不同的)return this.ch-node.ch;}return this.weight - node.weight;//默认排列升序}//重写toString方法
//效果:[ 字符 -> 010 ]@Overridepublic String toString() {//重写之后,等一下打印就可以直接用引用就可以了return "[" + this.ch + " -> " + this.code + "]  ";}}

步骤:

想要达到上述效果,大致可以分为这几步:

1、字符集转化成一个一个的键值对;

2、键值对转节点,节点放入一个集合

3、依据集合创建哈夫曼树。

4、对哈夫曼树的叶子节点进行哈夫曼编码

下面我们一点一点来解决


1、字符集转键值对

这里就要使用到HashMap了:

HashMap的

Key=一个字符

Value=权重(就是一个字符在字符集出现的频率,当然也不完全是,等一下会讲到)

public class Test {public static void main(String[] args) {Scanner in = new Scanner(System.in);System.out.println("输入的字符集:");String arr = in.nextLine();char[] chars = arr.toCharArray();//转化成字符数组Node root=HuffmanCoding.createTree(chars);//调用这个静态方法,具体实现看下一块代码      }}
class HuffmanCoding {public static Node createTree(char[] arr) {Map.Entry<Character, Integer>[] entries = charGetEntry(arr);//获取键值对}

charGetEntry()方法就是专门用来把字符集转化成一个一个的键值对的,然后返回这个类型:

Map.Entry<Character, Integer>[]

为什么是这个类型?

因为HashMap不能直接同时访问Character也就是字符,以及Integer接也就是对应字符的权重。

如果要访问键值对,需要调用HashMap的setEntry方法,setEntry方法会返回Map.Entry<Character, Integer>[]类型的数组

而Map.Entry中有访问和修改关键字和值的方法。

charGetEntry()方法:

private static Map.Entry<Character, Integer>[] charGetEntry(char[] arr) {//定义Hashmap储存不重复的键值对Map<Character, Integer> map = new HashMap<>(arr.length);//长度肯定不会超过arr的长度for (char ch : arr) {map.put(ch, 0);//权值默认先给0,等一下处理}//定义Entry[]这个集合,用来存放键值对Map.Entry<Character, Integer>[] entrys = new Map.Entry[map.size()];//长度刚好就是map的长度int i = 0;for (Map.Entry<Character, Integer> entry : map.entrySet()) {//遍历每一个entry,以此把每一个键值对放到集合entrys中entrys[i++] = entry;}//现在就可以赋值weight了i=entrys.length-1;//从后往前遍历,当然从前往后也可以while(i>=0){int n=0;for (int j = 0; j <arr.length ; j++) {if(entrys[i].getKey()==arr[j]){//两个字符一样,那么频率++n++;}}entrys[i--].setValue(n);}//程序到达这里,键值对已经储存完毕,下面直接返回集合即可return entrys;}

如下第一步完成:


2、键值对转节点

上一步我们已经获得了所有的键值对,储存在entries中,现在只需创建一个List<Node>类型的集合,获遍历entries,获取键值对即可:

        //放入节点中(用集合来管理)int length = entries.length;List<Node> nodeList = new ArrayList<>(length);//长度一定和entries是一样的while (length > 0) {//用Node的构造方法,创建结点,第一个参数是关键子,第二个参数是权重nodeList.add(new Node(entries[length - 1].getKey(), entries[length - 1].getValue()));length--;}

此时,第二步完成,nodeList就是一个储存着所有结点的集合。

3、构造哈夫曼树

此时类

HuffmanCoding

的静态方法createTree已经定义好了:

    public static Node createTree(char[] arr) {Map.Entry<Character, Integer>[] entries = charGetEntry(arr);//获取键值对,已经完成,保存在了entries中//放入节点中(用集合来管理)int length = entries.length;List<Node> nodeList = new ArrayList<>(length);//长度一定和entries是一样的while (length > 0) {nodeList.add(new Node(entries[length - 1].getKey(), entries[length - 1].getValue()));length--;}while (nodeList.size() > 1) {//只要大于一,就合并Collections.sort(nodeList);//先排升序,重写了用weight比较Node a = nodeList.remove(0);Node b = nodeList.remove(0);Node newNode = new Node(a.weight + b.weight);//这个是双亲节点newNode.left = a;newNode.right = b;nodeList.add(newNode);}return nodeList.remove(0);//还剩下的一个节点,就是哈夫曼树的根节点}

4、叶结点编码

下面运用的是递归,对叶子结点进行赋值0或者1(左结点是0,右结点时1):

 //这个函数可以把Node的code修改public static void coding(Node root, StringBuilder sb) {if (root == null) return;//如果只有一个节点,code==“”---->空字符串root.code = sb.toString();//先根节点if (root.left == null && root.right == null) {return;//直接返回}//如果不是叶子节点,那么一定有左右孩子----》因为这是哈夫曼树sb.append("0");//先左边,所以加一个0coding(root.left, sb);//递归sb.replace(sb.length() - 1, sb.length(), "1");//把最后一个替换成1,因为要走右边了coding(root.right, sb);//递归sb.delete(sb.length() - 1, sb.length());//也要删除,删除的区间是:左开右闭的!}

现在这个HuffmanCoding这个类就可以对一个字符集进行哈夫曼编码了,当然如果想要打印对应的值,需要写一个打印叶子结点的方法:

  //前序遍历打印叶子结点public static void showChar(Node root) {if (root == null) return;if (root.left == null && root.right == null) {//这是一个叶子节点,直接打印然后返回System.out.println(root);return;}//不是叶子结点,就遍历左右子树showChar(root.left);showChar(root.right);}

测试

最终哈夫曼编码类的源码:

class HuffmanCoding {public static Node createTree(char[] arr) {Map.Entry<Character, Integer>[] entries = charGetEntry(arr);//获取键值对,已经完成,保存在了entries中//放入节点中(用集合来管理)int length = entries.length;List<Node> nodeList = new ArrayList<>(length);//长度一定和entries是一样的while (length > 0) {nodeList.add(new Node(entries[length - 1].getKey(), entries[length - 1].getValue()));length--;}while (nodeList.size() > 1) {//只要大于一,就合并Collections.sort(nodeList);//先排升序,重写了用weight比较Node a = nodeList.remove(0);Node b = nodeList.remove(0);Node newNode = new Node(a.weight + b.weight);//这个是双亲节点newNode.left = a;newNode.right = b;nodeList.add(newNode);}return nodeList.remove(0);//还剩下的一个节点,就是哈夫曼树的根节点}private static Map.Entry<Character, Integer>[] charGetEntry(char[] arr) {//定义Hashmap储存不重复的键值对Map<Character, Integer> map = new HashMap<>(arr.length);//长度肯定不会超过arr的长度for (char ch : arr) {map.put(ch, 0);//权值默认先给0,等一下处理}//定义Entry[],又来放键值对(可以访问的)Map.Entry<Character, Integer>[] entrys = new Map.Entry[map.size()];//长度刚好就是map的长度int i = 0;for (Map.Entry<Character, Integer> entry : map.entrySet()) {entrys[i++] = entry;}//先在赋值weighti=entrys.length-1;while(i>=0){int n=0;for (int j = 0; j <arr.length ; j++) {if(entrys[i].getKey()==arr[j]){//两个字符一样,那么频率佳佳n++;}}entrys[i--].setValue(n);}//程序到达这里,键值对已经储存完毕return entrys;}//这个函数可以把Node的code修改public static void coding(Node root, StringBuilder sb) {if (root == null) return;//如果只有一个节点,code==“”---->空字符串root.code = sb.toString();//先根节点if (root.left == null && root.right == null) {return;//直接返回}//如果不是叶子节点,那么一定有左右孩子----》因为这是哈夫曼树sb.append("0");//先左边,所以加一个0coding(root.left, sb);//递归sb.replace(sb.length() - 1, sb.length(), "1");//把最后一个替换成1,因为要走右边了coding(root.right, sb);//递归sb.delete(sb.length() - 1, sb.length());//也要删除,删除的区间是:左开右闭的!}//前序遍历打印叶子结点public static void showChar(Node root) {if (root == null) return;if (root.left == null && root.right == null) {//这是一个叶子节点,直接打印然后返回System.out.println(root);return;}//不是叶子结点,就遍历左右子树showChar(root.left);showChar(root.right);}}

类中静态方法使用的演示:

关于哈夫曼编码的解码

会了编码,其实解码就很容易了

值得注意的是:

不同的字符集合,对应的哈夫曼编码是有所差异的

所以如果要进行解码,那么必须直到每一个字符对应出现的频率

解码思路:

在接收到字符频度表之后,创建一颗哈夫曼树,每次从root结点开始遍历,从第一个字符开始,是0就往左树走,是1就往右走,直到叶子结点即可解码。

具体实现:

    public static void deCoding(Map.Entry<Character, Integer>[] arrEntry, String arr) {//需要接收字符频度表 and 哈夫曼编码Node Cur= createTree(arrEntry);//调用了重载的创建哈夫曼树的方法char[] chars = arr.toCharArray();int cur = 0;//表示读取编码的位置Node root=Cur;//Cur保存了哈夫曼树的根结点while (cur < chars.length) {//读完就退出循环while (root.left != null && root.right != null) {//没有到叶子结点就一直循环if (chars[cur] == '1') {//是1走右边cur++;root = root.right;} else {//是二走左边cur++;root = root.left;}}//如果跳出了循环说明已经是叶子结点了System.out.println(root);root=Cur;}}

因为我们在createTree()方法中传入的类型是Map.Entry<Character, Integer>[],

所以需要对createTree()方法,

进行一次重载,

这个重载其实不麻烦,只要改一下接口,就可以了:

    private static Node createTree(Map.Entry<Character, Integer>[] entries){//重载方法,用在解码时调用这个方法//放入节点中(用集合来管理)int length = entries.length;//只改了这一行,和方法的唯一参数!!!!!List<Node> nodeList = new ArrayList<>(length);//长度一定和entries是一样的while (length > 0) {nodeList.add(new Node(entries[length - 1].getKey(), entries[length - 1].getValue()));length--;}while (nodeList.size() > 1) {//只要大于一,就合并Collections.sort(nodeList);//先排升序,重写了用weight比较Node a = nodeList.remove(0);Node b = nodeList.remove(0);Node newNode = new Node(a.weight + b.weight);//这个是双亲节点newNode.left = a;newNode.right = b;nodeList.add(newNode);}return nodeList.remove(0);//还剩下的一个节点,就是哈夫曼树的根节点}

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

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

相关文章

管理学SCI期刊,中科院4区,审稿快易录用,性价比超高!

一、期刊名称 Central European Journal of Operations Research 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;管理学 影响因子&#xff1a;1.7 中科院分区&#xff1a;4区 出版方式&#xff1a;订阅模式/开放出版 版面费&#xff1a;选择开放出版需…

人大金仓报The connection attempt failed.Reason:Connection reset解决办法

在连接人大京仓数据库 的时候报下面的错误 解决办法&#xff1a; 更换这里的IP地址就行&#xff0c;不要用127.0.0.1&#xff0c;然后就可以了

24.c++异常(异常的抛出和捕获、异常的重新抛出、抛出异常对象、抛出派生类对象、异常规范)

1.C语言传统的处理错误的方式 传统的错误处理机制&#xff1a; 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以接受。如发生内存错误&#xff0c;除0错误时就会终止程序。返回错误码&#xff0c;缺陷&#xff1a;需要程序员自己去查找对应的错误。如系统…

vscode的git插件使用教程

虽然git的命令我没有滚瓜烂熟&#xff0c;但vscode的git插件是尊嘟很好用啊&#xff0c;都被我用烂了。在网上看见一个讲的很不错的插件教程。借鉴一下。并在一些地方用块引用进行了补充说明&#xff01; 跳过了vscode安装过程。 克隆GitHub中的存储库&#xff1a; 1、复制Gi…

ai智能机器人电销的发展现状如何?

在移动互联网时代&#xff0c;人们对于营销的需求越来越高&#xff0c;而传统的营销方式已经无法满足人们的需求。下面我们来看看智能机器人电销的发展现状如何&#xff1f; 智能机器人电销作为一种全新的营销方式&#xff0c;正在迅速崛起。据市场机构统计&#xff0c;未来几…

C语言中的关键字static和extern

Hello,亲爱的小伙伴们&#xff0c;我又来了&#xff0c;上一期作者菌讲解了C语言中函数的知识点&#xff0c;得到了很好的反馈&#xff0c;这里作者菌感谢每一个至此我的小伙伴&#xff01;&#xff01;今天作者菌又来补充一些很有用的知识&#xff0c;感兴趣的uu们不要吝啬手中…

HTML学习|网页基本信息、网页基本标签、图像标签、超链接标签、列表标签、表格标签、媒体元素、页面结构分析、iframe内联框架

网页基本信息 DOCTYPE是设置使用什么规范&#xff0c;网页整个信息都在html标签中&#xff0c;head标签里包含字符集设置&#xff0c;网页介绍等信息&#xff0c;title标签是网页的名称&#xff0c;网页的主干都在body标签中 网页基本标签 标题标签 h1~h6都是标题标签&#x…

UE进阶篇四:多语言本地化

多语言 1、创建字符串表格&#xff1a;可以通过导入/导出CSV文件编辑或者编辑器直接编辑 2、工具 -> 本地化控制板 收集引擎中文本 3、添加新语言, 导出 .po文件&#xff0c;文本编辑也可直接编辑器编辑 4、使用方法 4.1 FText直接添加本地化 4.2 蓝图调用 4.3 C调用 5、…

鸿蒙开发-ArkTS语言-容器-非线性容器

鸿蒙开发-UI-web 鸿蒙开发-UI-web-页面 鸿蒙开发-ArkTS语言-基础类库 鸿蒙开发-ArkTS语言-并发 鸿蒙开发-ArkTS语言-并发-案例 鸿蒙开发-ArkTS语言-容器 文章目录 前言 一、非线性容器 1.HashMap 2.HashSet 3.TreeMap 4.TreeSet 5.LightWeightMap 6.LightWeightSet 7.P…

(一)Linux的vim编辑器的使用

一.vim编辑器 Vim 是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。简单的来说, vi 是老式的字处理器,不过功能已经很齐全了,但是还是有可以进步的地方。 vim 则可以说是程序开发者的一项很好用的工具。 二…

活动倒计时!四城再度联动,盘古信息与您相约上海|重庆|东莞|合肥

五月&#xff0c;是生命的复苏季&#xff0c;是希望的播种月。在这个充满活力的时节&#xff0c;盘古信息将在上海、重庆、东莞、合肥四地盛大举办四场精彩纷呈的活动。届时&#xff0c;我们将呈现一系列精心打造的工业软件产品及解决方案&#xff0c;带您领略制造业数字化转型…

深入入IAEA底层LinkedList

✅作者简介&#xff1a;大家好&#xff0c;我是再无B&#xff5e;U&#xff5e;G&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;再无B&#xff5e;U&#xff5e;G-CSDN博客 目标&#xff1a; 1.掌握LinkedList 2.…

ESG榜单冲击数据集(2000-2022年)

参照《财经研究》中刘柏&#xff08;2024&#xff09;的做法&#xff0c;以2015年为中点&#xff0c;根据商道荣绿2015年6月公开的ESG榜单数据&#xff0c;构建ESG榜单冲击的DID数据&#xff0c;如果公司属于ESG榜单冲击的公司&#xff0c;且年份≥2015&#xff0c;则为1&#…

母婴店运用商城小程序店铺的效果是什么

母婴市场规模高&#xff0c;还可与不少行业无缝衔接&#xff0c;尤其是以90后、00后为主的年轻人&#xff0c;在备孕生育和婴儿护理前后等整体流程往往不惜重金且时间长&#xff0c;母婴用品无疑是必需品&#xff0c;商家需要多方面拓展全面的客户及打通场景随时消费路径。 运…

华为OD机试【全量和已占用字符集】(java)(100分)

1、题目描述 给定两个字符集合&#xff0c;一个是全量字符集&#xff0c;一个是已占用字符集&#xff0c;已占用字符集中的字符不能再使用。 2、输入描述 输入一个字符串 一定包含&#xff0c;前为全量字符集 后的为已占用字符集&#xff1b;已占用字符集中的字符一定是全量…

Linux网络部分——部署YUM仓库及NFS共享服务

目录 一、yum仓库服务 1. 软件仓库的提供方式 2.如何构建并使用ftp软件仓库&#xff08;与本地yum源方法一致&#xff09; 3.如何搭建使用yum在线源&#xff1f; 4.yum软件包下载如何保存&#xff1f; 二、NFS共享存储服务 1.存储类型 2.提供共享存储的组合 3.NFS网络…

山东齐鲁文化名人颜廷利:教育的本质区别重点是什么

教育的本质区别重点是‘方式’&#xff0c; 现在的教育却成为了一种‘形式’&#xff1b; 教育的核心价值关键载于‘实践’&#xff0c; 当前我们的教育观念却变成了消耗‘时间’&#xff1b; ‘读书’的原则在于‘堵疏’&#xff0c;作为汉语‘堵疏’一词&#xff0c;顾名思义…

掌握学习平台中的高效学习技巧

学习在我们生活中起着至关重要的作用。随着科技的发展&#xff0c;越来越多的学习平台为我们提供了更广阔的学习机会。然而&#xff0c;要实现高效学习&#xff0c;我们需要掌握一些技巧。 规划学习目标 首先&#xff0c;一个明确的学习目标是高效学习的基础。在使用学习平台…

如何将jsp项目转成springboot项目

昨天说过&#xff0c;springboot推荐使用Thymeleaf作为前后端渲染的模板引擎&#xff0c;为什么推荐用Thymeleaf呢&#xff0c;有以下几个原因&#xff1a; 动静结合&#xff1a;Thymeleaf支持HTML原型&#xff0c;允许在HTML标签中增加额外的属性来实现模板与数据的结合。这样…

Redis缓存雪崩,击穿,穿透问题

缓存雪崩、击穿、穿透、发生的背景 ​ 作者最近在写一个社区论坛项目&#xff0c;初始设想将论坛里用户发布的帖子内容存到数据库中&#xff0c;当用户访问论坛里的帖子时&#xff0c;帖子信息都从数据库中查。众所周知数据库的帖子数据是存在磁盘中的&#xff0c;而磁盘读写数…