霍夫曼树——霍夫曼编码

article/2025/9/24 16:15:02

霍夫曼编码

基本介绍

  1. 霍夫曼编码是一种编码方式,属于一种程序算法
  2. 霍夫曼编码是霍夫曼树在通讯领域的经典应用之一
  3. 霍夫曼编码广泛用于数据文件的压缩,压缩率通常在20% 到90%,通常数据的重复率越高,那么压缩率就越高
  4. 霍夫曼编码是可变字长编码(VLC)的一种,由霍夫曼提出,又称之为最佳编码

通讯领域中常用的编码方式

  1. 定长编码
  • 对原始数据不加任何的修改,原原本本按照对应的编码集,转成二进制代码进行存储。占用空间大。
    在这里插入图片描述
  1. 变长编码
  • 按照字符出现的次数进行编码,原则上是出现的次数越多,则编码越小。
  • 是比较简洁,但是会出现多义性,因为代码彼此之间有重复。
  • 前缀编码:字符的编码不是其余字符编码的前缀。
    在这里插入图片描述
  1. 霍夫曼编码
  • 对所给字符串的各个字符出现的个数进行统计
  • 按照上面字符出现的次数构建霍夫曼树,次数作为权值,同时你的节点上要有所代表的字符。
  • 根据霍夫曼树给各个字符进行编码:
    • 向左的路径为0,向右的路径为1
    • 根据到达给节点的路径确定该节点代表字符的编码
    • 生成的编码是前缀的编码,每一个编码都不会是另外一个字符的前缀。致使在匹配的时候不会出现多义性
  • 按照上面的霍夫曼编码,将原先的字符换转换成编码。此编码是前缀编码,不会出现多义性,同时又对在定长编码编译情况下原长码进行压缩处理,是无损压缩。
  • 在这里插入图片描述
  • 问题:当有大量的字符是相同的出现次数时,相同权值的不同叶子节点可能排在不同的位置,形成不同的霍夫曼树,故而形成不同的代码,但是WPL始终是相同的,所以没有必要担心。最终形成的压缩文件的大小一定相同,因为WPL始终是相同的。

思路分析

  1. 梗概:将每一个字符本身的ASCII码和其出现的次数两者结合,以出现的次数为权构建霍夫曼树,再根据到达对应节点的路径生成对应的霍夫曼编码。
  2. 总体步骤:构建相应的霍夫曼树——》遍历读取生成相应的霍夫曼编码
  3. 具体分析
  • 节点类:在满足基本节点的特征的基础上,必须把出现的次数作为权值,结点必须包含其本身代表的字符的ASCII码值。同时涉及到构建霍夫曼值,所以必须实现Comparable接口
  • 主类中:
    • 将所给的字符串生成字节型数组
    • 将字节型数组根据自身的内容和出现的次数,生成对应的Node结点(用Map类处理计数),然后转成包含Node的集合类对象list
    • 生成霍夫曼树
    • 遍历霍夫曼树生成对应的霍夫曼编码

代码实现

  1. Code类:
class Node implements Comparable<Node>{Byte date;//表示字符对应的ASCII码int weight;//表示对应字符出现的次数Node left;Node right;public Node(Byte date, int weight) {this.date = date;this.weight = weight;}@Overridepublic int compareTo(Node o) {return this.weight - o.weight;}@Overridepublic String toString() {return "Node{" +"date=" + date +", weight=" + weight +'}';}public void preOrder(){if (this != null){System.out.println(this);}if (this.left != null){this.left.preOrder();}if (this.right != null){this.right.preOrder();}}
}
  1. 主类中
  • 统计各字符出现的次数,并将之转换成对应的集合类
private static List<Node> getNodes(byte[] bytes){List<Node> list = new ArrayList<Node>();//创建对应的集合类,用来创建霍夫曼树---每一个对象是节点类Node//遍历对应的生成的Byte【】数组,然后统计一下各个字符出现的次数---用Map类,键值对数据Map<Byte,Integer> counts = new HashMap<Byte,Integer>();for (byte ch : bytes){Integer count = counts.get(ch);//获取出现字节对应的次数if (count == null){//如果对应的Integer是空,说明第一次出现counts.put(ch,1);//再map类中,put是添加,put也是修改}else{//如果对应的Integer不为空,说明已经录入过相应的数字//所以将原来的数字加1counts.put(ch,count + 1);}}//遍历Map类中的每一个键值对,将之生成对应的list类对象for(Map.Entry<Byte,Integer> entry:counts.entrySet()){list.add(new Node(entry.getKey(),entry.getValue()));//key是对应的字符,value是对应的出现的次数}return list;}
  • 根据生成的集合类,整理生成对应的霍夫曼树
private static Node huffmanTree(byte[] bytes){List<Node> list = getNodes(bytes);Node left;Node right;while (list.size() > 1){Collections.sort(list);left = list.get(0);list.remove(left);right = list.get(0);list.remove(right);Node parent = new Node(null,left.weight + right.weight);parent.left = left;parent.right = right;list.add(parent);}return list.get(0);}
  • 遍历霍夫曼树生成各个叶子节点的对应的路径
static Map<Byte,String> huffmanCodes = new HashMap<Byte,String>();
//存放霍夫曼编码的Map类,公共变量,每一次递归都要存储,所以静态变量
static StringBuilder stringBuilder = new StringBuilder();
//拼接字符串的起点,根节点对应的拼接点
/*** 功能描述:将传入的node节点的所有叶子结点的霍夫曼编码并且放入到霍夫曼编码集合中** @param node 对应的传入待处理的节点,* @param code  分别代码不同方向路径的值,左子节点为0,右子节点为1* @param stringBuilder   用于拼接路径,是不是说一个每一条路都对应了一个个stringbuilder*/private static void getCodes(Node node,String code,StringBuilder stringBuilder){//首先用你传入的Stringbuilder构建一个StringbuilderStringBuilder stringBuilder1 = new StringBuilder(stringBuilder);//一开始就两条路,那就两个,然后每一次走不通的路都会生成不同的值,// 同时还是在上一个节点的基础上进行生成stringBuilder1.append(code);//已经在原先的基础上生成了一个对应的一个stringbuilder,而选择不同的路径也要增加一个code值,代表我走的这条路if (node != null){//如果node等于空就不去处理,说明已经到达结尾,不为空才有相关的值进行判断// 判断当前节点是叶子节点还是非叶子节点if (node.date == null){//说明是非叶子结点,因为本身就没有代表字母//向两边进行递归getCodes(node.left,"0",stringBuilder1);getCodes(node.right,"1",stringBuilder1);}else{//data不为空,说明是叶子节点,进行存储huffmanCodes.put(node.date,stringBuilder1.toString());}}}
  • main方法:
 public static void main(String[] args) {String content = "I like Java.Do you like Java?";byte[] contentBytes = content.getBytes();//字符串和字节的区别是什么?字节就是将字符转成为对应的ASCII码,字符就是输出字符的本身getCodes(huffmanTree(contentBytes),"",stringBuilder);System.out.println("生成的霍夫曼编码表" + huffmanCodes);
}

分析与总结

  1. 处理处理存在映射关系的两组数据,用Map类,既便于计算出现次数,又便于形成一一对应的关系,用于翻译输出,所以整个过程有两次转换,第一次是在统计各个字符出现的次数,将字节型数组转成Map类;第二次是在由霍夫曼树生成霍夫曼编码时,由树生成Map的编码。
  2. 在形成编码的过程中,“边走边成路!”,每一次走不同的结点都会迭代之前的路径生成对应的不同的路径,而路径本身又生成对应的编码。关键点还是还设置一个共有的起点Stringbuilder,共有的记录册HashMap HuffmanCode
  3. 总的过程:字符串 —— 》字节型数组——》HashMap<Byte,String>——》ArrayList list——》霍夫曼树——》霍夫曼编码
  4. 对于集合类的操作很不流畅,都忘得差不多了,尤其是在遍历键值对的部分关于entry。

复习代码:

package huffmancode;import java.util.*;public class HuffmanCode2 {public static void main(String[] args) {String str = "I have no choice but I have to do!I believe in myself!";byte[] bytes = str.getBytes();//然后将字节型数组生成对应的节点类//将节点类生成霍夫曼树//有霍夫曼树生成对应的霍夫曼编码System.out.println(acquireHuffmanCode(bytes));}//改良,连续调用方法,我自己都晕,将所有的方法封装起来进行调用public static Map<Byte,String> acquireHuffmanCode(byte[] bytes){getHuffmanCode(getHuffmanTree(getNode(bytes)),"",stringBuilder);return HuffmanCode;}//第三部分,有对应的霍夫曼树生成对应的霍夫曼编码//编码Map类承装,设置一个地图碎片的蓝本,用于拼接static Map<Byte,String> HuffmanCode = new HashMap<Byte,String>();static StringBuilder stringBuilder = new StringBuilder();private static void getHuffmanCode(Node2 node2,String code,StringBuilder stringBuilder){StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);stringBuilder1.append(code);if (node2 != null){//说明当前节点不为空,可以斤西瓜下一步的递归操作//节点类型有两种,叶子节点和非叶子节点if (node2.ch == null){//非叶子节点进行左右递归,并且不用判定左右节点是否为空getHuffmanCode(node2.left,"0",stringBuilder1);getHuffmanCode(node2.right,"1",stringBuilder1);}else{//是叶子节点说明已经递归完毕HuffmanCode.put(node2.ch,stringBuilder1.toString());}}}//根据生成的集合list整理生成对应的霍夫曼树private static Node2 getHuffmanTree(List<Node2> list){//生成霍夫曼树就两步,不断重复,排序,建树while (list.size() > 1){//排序Collections.sort(list);Node2 left = list.get(0);Node2 right = list.get(1);list.remove(left);list.remove(right);Node2 parent = new Node2(null,left.val + right.val);parent.left = left;parent.right = right;list.add(parent);}return list.get(0);}//将字节型数组生成对应的节点类private static List<Node2> getNode(byte[] bytes){List<Node2> list = new ArrayList<Node2>();//创建一个集合用来存储转换过后生成的节点Map<Byte,Integer> counts = new HashMap<Byte,Integer>();//调用Map集合类,利用其键值对的存储方式,便于统计书出现次数//遍历字节数组,调用Map类,进行统计for (byte ch:bytes){Integer count = counts.get(ch);//第一次输入,看看是否有对应的数字if (count != null){//如果count不为null,说明已经遍历过了,可以进一部存储counts.put(ch,count + 1);}else{//如果count为null,说明没有遍历过,是第一次出现counts.put(ch,1);}}//统计完数据之后,用来创建对应的节点类for (Map.Entry<Byte,Integer> enty:counts.entrySet()){//说明counts是可以当作数组来及进行遍历list.add(new Node2(enty.getKey(),enty.getValue()));}return list;}}
class Node2 implements Comparable<Node2>{public Byte ch;//对应的字符的字节码int val;//对应字符出现的次数Node2 left;Node2 right;public Node2(Byte ch, int val) {this.ch = ch;this.val = val;}@Overridepublic String toString() {return "Node2{" +"ch=" + ch +", val=" + val +'}';}@Overridepublic int compareTo(Node2 o) {return this.val - o.val;}
}

http://chatgpt.dhexx.cn/article/NoTrGOfS.shtml

相关文章

【数据结构】图解霍夫曼编码,看了就能懂

今天来给大家普及一下霍夫曼编码&#xff08;Huffman Coding&#xff09;&#xff0c;一种用于无损数据压缩的熵编码算法&#xff0c;由美国计算机科学家大卫霍夫曼在 1952 年提出——这么专业的解释&#xff0c;不用问&#xff0c;来自维基百科了。 说实话&#xff0c;很早之前…

霍夫曼编码原理以及代码实现

霍夫曼编码压缩能够实现对于自然语言文件空间大幅压缩。对于普通的文本文件字符&#xff0c;简单起见&#xff0c;如果字符为ASCII&#xff0c;则文本中的每个字符使用7bit来表示&#xff0c;如果文本中有大量的重复相同序列&#xff0c;使用ASCII编码来保存存储会造成大量的空…

霍夫曼编码(huffman coding) (java实现)

文章目录 一、浅谈赫夫曼编码二、获取赫夫曼编码1.获取字符出现的次数2.创建赫夫曼树3.指定编码 三、代码实现1.指定编码代码2.完整代码 总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、浅谈赫夫曼编码 赫夫曼编码(Huffman Coding)&#xff0c…

霍夫曼树:霍夫曼编码(Huffman Tree:Huffman Coding)

一、简介 霍夫曼树常处理符号编写工作。根据整组数据中符号出现的频率高低&#xff0c;决定如何给符号编码。如果符号出现的频率越高&#xff0c;则给符号的码越短&#xff0c;相反符号的号码越长。 相关术语 路径&#xff1a;从书中一个节点到另一个节点之间的分支构成这两个…

霍夫曼编码

霍夫曼在1952年提出了霍夫曼编码&#xff0c;霍夫曼编码是一种无损的统计编码方法&#xff0c;利用信息符号概率分布特性来改编字长进行编码。适用于多元独立信源。霍夫曼编码对于出现概率大的信息符号用字长小的符号表示&#xff0c;对于出现概率小的信息用字长大的符号代替。…

霍夫曼(Huffman)编码算法详解之C语言版

一、Huffman编码 霍夫曼(Huffman)树是一类带权路径长度最短的二叉树树。Huffman树的一个非常重要的应用就是进行Huffman编码以得到0-1码流进行快速传输。 在电报收发等数据通讯中&#xff0c;常需要将传送的文字转换成由二进制字符0、1组成的字符串来传输。为了使收发的速度提…

哈夫曼编码

哈夫曼编码 概念前缀码的二叉树及权值哈夫曼编码的设计思想 实例伪代码 概念 哈夫曼编码是一种字符编码方式&#xff0c;是可变长编码的一种&#xff0c;1952年提出&#xff0c;依据字符在文件中出现的频率来建立一个用0,1串表示各字符&#xff0c;使平均每个字符的码长最短的…

图像处理—霍夫曼编码

图像压缩编码是专门研究图像数据压缩的技术&#xff0c;就是尽量减少表示数据图像所需要的数据量。 本章主要介绍图像压缩编码的基础知识&#xff0c;重点讲解常用的图像压缩编码方法&#xff0c;如霍夫曼编码、香农编码、算术编码、行程编码和预测编码及编码方法的MATLAB实现&…

哈夫曼编码(理解)

基础理解 什么是哈夫曼树&#xff08;Huffman Tree&#xff09; 给定N个带权值的叶子节点&#xff0c;如何构造出一个带权路径最小的二叉树&#xff1f; 在数据结构理论中&#xff0c;哈夫曼树又称为最优树&#xff0c;相关的知识点还有哈弗曼编码等。在正式介绍哈夫曼树之前…

学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!

CSDN 的学弟学妹们&#xff0c;大家好&#xff0c;我是沉默王二。 今天来给大家普及一下霍夫曼编码&#xff08;Huffman Coding&#xff09;&#xff0c;一种用于无损数据压缩的熵编码算法&#xff0c;由美国计算机科学家大卫霍夫曼在 1952 年提出——这么专业的解释&#xff…

哈夫曼编码详解

一&#xff1a;基本介绍 哈夫曼编码也翻译为 赫夫曼编码(Huffman Coding)&#xff0c;又称霍夫曼编码&#xff0c;是一种编码方式, 属于一种程序算法 赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一。 赫夫曼编码广泛地用于数据文件压缩。其压缩率通常在20%&#xff5…

赫夫曼编码

一 基本介绍 1 赫夫曼编码也翻译为哈夫曼编码(Huffman Coding)&#xff0c;又称霍夫曼编码&#xff0c;是一种编码方式, 属于一种程序算法。 2 赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一。 3 赫夫曼编码广泛地用于数据文件压缩。 其压缩率通常在20%&#xff5e;9…

哈夫曼编码(Huffman Coding)原理详解

哈夫曼编码 哈夫曼编码&#xff0c;又称为哈夫曼编码&#xff08;Huffman Coding&#xff09; 是一种可变长编码&#xff08; VLC, variable length coding)&#xff09;方式&#xff0c;比起定长编码的 ASCII 编码来说&#xff0c;哈夫曼编码能节省很多的空间&#xff0c;因…

霍夫曼编码详解

本专栏包含信息论与编码的核心知识&#xff0c;按知识点组织&#xff0c;可作为教学或学习的参考。markdown版本已归档至【Github仓库&#xff1a;information-theory】&#xff0c;需要的朋友们自取。或者公众号【AIShareLab】回复 信息论 也可获取。 文章目录 霍夫曼编码最佳…

霍夫曼编码(Huffman Coding)

霍夫曼编码(Huffman Coding)是一种编码方法,霍夫曼编码是可变字长编码(VLC)的一种。 霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用…

Typecho 博客美化

以前用typecho博客&#xff0c;现在学生服务器到期了&#xff0c;先记录到csdn上&#xff0c;以后工作有钱了&#xff0c;再租个服务器写博客:) 代码高亮 https://www.typechodev.com/plugin/482.html 天气功能 使用心知天气带的插件就可以轻松实现了。 https://www.seniverse…

Hexo博客之博客美化

https://lqgjava.github.io/2019/08/24/Hexo博客之博客美化/ 只需阅读这一篇文章&#xff0c;就可以让你的博客变得丰富多彩&#xff0c;有添加卡通人物&#xff0c;添加鼠标点击爱心&#xff0c;添加鼠标指针样式 添加彩色滚动变换、添加背景音乐、添加动态彩带等&#xff0…

CSDN博客美化

排列博客分类 管理博客-分类专栏下修改 双击分类名称&#xff0c;即可编辑&#xff0c;输入“#”“空格”“分类名称”可将一级分类改成二级分类。 显示的效果 分类图标设置 编辑-设置图标&#xff0c;可以这里找阿里素材库 效果图

【CSDN】CSDN博客美化教程

一、效果 二、MarkDown简明教程 MarkDown简明教程 三、摘要 添加摘要&#xff0c;增加可读性。 PS. 其实&#xff0c;可以根据深度学习算法&#xff0c;自动生成摘要。 四、自定义博客栏目 五、修改皮肤

博客美化作业详细教程

博客美化作业 第一步 下载作业资源 第二步 在文件夹内打开下载资源 单击后缀为HTML文件的网页文件&#xff0c;右击用编译器打开 这些软件都可以&#xff08;除了紫色的&#xff09; 第三步 准备工作 首先让我们重新看下作业要求&#xff1a; 必须使用“类选择器”来美化网…