java哈夫曼_用 JAVA 实现哈夫曼树(Huffman Tree)
-1. 什么是树
树是一种 有层次关系的 数据结构。它由结点组成。
图一:
树的结点由 数据域 和 子结点域 组成。数据域 作为数据的容器;子结点域 存放 子结点 的地址。一个结点是它的子结点的父结点。不同层级之间的结点通过 子结点域 形成 “父子关系”。
每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;没有 子结点的结点称为叶结点。
图一中的结点A 就是根结点。B、C是 A 的子结点。C、E、F三个结点组成了一个子树。E也是一个子树。D、E、F都是叶结点。
如果一个结点有 n 个子结点,那么这个 结点的度 是 n 。如果一个树中的结点的度的最大值是 n ,那么这个 树的度 是 n。
对于一个树中的任意两个不同的结点,如果从一个结点出发,按层次 自上而下 沿着一个个结点能到达另一结点,称它们之间存在着一条 路径。
根结点的 层次数 为1,每个结点的 层次数 都等于其父结点的 层次数 加一。
如果一个树中的结点的层次的最大值为 n,则这个树的 深度 为 n。
特别地,度为 2 的树称为 二叉树。
二叉树的结点类:
public class TreeNode {
public Object data; //数据域
public TreeNode left; //左子结点域
public TreeNode right; //右子结点域
}
0. 什么是哈夫曼树
哈夫曼树 (Huffman Tree,霍夫曼树,最优二叉树) 是一种二叉树。哈夫曼树是一种带权路径长度最短的树。
权 是与树的结点关联的一个实数,用 W 表示。非叶结点的 权 等于其各子结点的 权 的和。一个结点的 路径长度 是这个结点与根结点的层次数之差,也就是该结点的层次数减 1,用 l 表示。
结点的带权路径长度 为:从根结点到该结点之间的路径长度与该结点的权的乘积。
一个二叉树中的各个 叶结点 的 带权路径长度 之和为 树的带权路径长度 ,用WPL表示,即:
下图就是一个哈夫曼树。方框中的数表示结点的权。
下面这个树的带权路径长度 为
WPL = 12 × 3 + 14 × 2 + 21 × 2 + 33 × 2 = 172 。
7. 哈夫曼树的应用
最常见的应用之一应该是压缩。在英文中,字母 e 的出现频率最高,字母 z 的出现频率最低。如果我们用 1 bit 来表示字母 e ,那就节省了很多空间。
上面的想法很好,但是不可行。如果用 1 bit 的数据来表示字母 e ,比如说,用 0 来表示 e ,那么在解码的时候,很难从一串 0 和 1 中分辨出到底哪个 0 才是字母 e 对应的数据,因为其他的字母的编码也可能会含有 0 。
我们要做的是:保证每个字符的编码都不是其他任何字符编码的前缀,以至于不引起歧义;又保证每个字符的二进制编码足够短,以至于能够节省最多空间。
戴维·阿尔伯特·哈夫曼(David Albert Huffman, 1925-1999) 是一个天才。他解决了上面的问题。他对他的二叉树进行 编码:他把父结点到左子结点的路径编码为 0 ,把父结点到右子结点的路径编码为 1;在结点的数据域中存放字母,把字母的出现频数设为结点的权。在哈夫曼树中,把根结点到叶结点经过的路径的编码组合在一起,就得到了这个叶结点的哈夫曼编码。
举个例子:在一段文字中,字符 c 、I、M、r 分别出现了 8 次、5 次、3 次、2 次,没有其他字符。我们就把这三个字符存入叶结点的数据域,把它们的频数作为结点的权,建成哈夫曼树。
那么在这个哈夫曼树中, c 的哈夫曼编码就是 1,M 、r、I 的哈夫曼编码分别是 000、001、01。这是不引起歧义,又能节省最多空间的编码方案。
1. 结点类
public class Node {
public char data;//数据域
public int weight;//权
public Node left;//左子结点
public Node right;//右子结点
public Node(char data, int weight) {
this.data = data;
this.weight = weight;
}
public Node(Node left, Node right) {
this.left = left;
this.right = right;
weight = left.weight + right.weight;
}
}
2. 读取字符串
public class HuffmanTree {
private ArrayList nodeList = new ArrayList<>(48);
private int size;
private int[] classify(String data) {
int[] counter = new int[256];(注 1)
for (int i = 0; i < data.length; i++) {
counter[data.charAt(i)]++;
}
return counter;
}
//...
}
注 1:为什么是 256?
这里我们先把字符串中的各个字符分拣出来。为每一种字符都设一个计数器。比如说,A 的 ASCII 码是 97 ,那就把 counter[97] 作为 A 的计数器,每找到一个 A ,就给 counter[97] 增加 1。
ASCII 码中只有 256 个字符,所以把数组大小设为 256。这个 data 中就必须只含有普普通通的英文字母和英文符号。
3. 创建结点
为出现过的字符创建结点。把字符存入数据域,把频数设为权。把结点存入一个数组列表中。
private ArrayList createNode(int[] counter) {
ArrayList list = new ArrayList<>(50);
for (int i = 0; i < counter.length; i++) {
if (counter[i] > 0) {
list.add(new Node((char) i, counter[i]);
}
}
return list;
}
4. 按权排序
按权由小到大的顺序,对结点进行排序。
5. 建树
private void createTree(ArrayList list) { // list 中是已经排好序的结点
while (list.size() > 1) { // 最后剩下的一个结点就是根结点
Node left = list.remove(0), right = list.remove(0);
list.add(new Node(left, right));
}
}
6. 输出哈夫曼编码
依照实际情况,我们只要输出叶结点的哈夫曼编码就行了。
public void printCode(Node root) {
printCode(root, "");
}
private void printCode(Node node, String code) {
if (node.left != null) {
printCode(node.left, code + "0");
}
if (node.right != null) {
printCode(node.right, code + "1");
}
if (node.left == null && node.right == null) { // 如果是叶结点,就输出。
System.out.println(code + "\t" + node.data);
}
}
8. 我的代码
我写了一个接口 TestText, 里面只有一个属性 testText (StringBuilder)。存放的是测试用的字符串。
测试用的字符串内容如下:
"My son, the day you were born, " +
“the very forests of Paektu Mountain whispered the name \“Kim Jong-un\”.\n” +
“My child, I watched with pride as you grew into an heir of Juche.\n” +
“Remember - our family has always ruled with propaganda and violence.\n” +
“And I know you will show happiness when exercising your nuclear weapon.\n” +
“But the truest victory is controlling the minds of your people.\n” +
“I tell you this, for when my days have come to an end - you, shall be King.”
下面是我的代码:
public class HuffmanTree implements TestText {
private ArrayList nodeList;
public TreeNode root;
private int size;
public static void main(String[] args) {
HuffmanTree sample = new HuffmanTree(testText);
sample.printCode(sample.root);
}
public HuffmanTree(StringBuilder string) {
sortByWeight(nodeList = createNodeList(classify(string)));
size = nodeList.size();
root = build();
}
public void printCode(TreeNode root) {
printCode(root, "");
}
private void printCode(TreeNode node, String string) {
if (node.left != null) {
printCode(node.left, string + 0);
}
if (node.right != null) {
printCode(node.right, string + 1);
}
if (node.left == null && node.right == null) {
System.out.println(node.data + "\t" + string + "\t" + node.weight);
}
}
private TreeNode build() {
while (nodeList.size() > 1) {
nodeList.add(new TreeNode(nodeList.remove(0), nodeList.remove(0)));
}
return nodeList.get(0);
}
public void print() {// for test
int length = nodeList.size();
for (int i = 0; i < length; i++) {
System.out.println(nodeList.get(i));
}
}
private void sortByWeight(ArrayList list) {
int length = list.size(), mark;
TreeNode lightest;
for (int i = 0; i < length; i++) {
lightest = list.get(mark = i);
for (int j = i + 1; j < length; j++) {
if (lightest.weight > list.get(j).weight) {
lightest = list.get(mark = j);
}
}
if (mark > i) {
list.swap(i, mark);
}
}
}
private ArrayList createNodeList(int[] originalData) {
ArrayList r = new ArrayList<>(50);
int length = originalData.length;
for (int i = 0; i < length; i++) {
if (originalData[i] > 0) {
r.add(new TreeNode((char) i, originalData[i]));
}
}
return r;
}
private int[] classify(StringBuilder data) {
int[] r = new int[256];
int length = data.length();
for (int i = 0; i < length; i++) {
r[data.charAt(i)]++;
}
return r;
}
}
java哈夫曼_用 JAVA 实现哈夫曼树(Huffman Tree)相关推荐
- java项目----教务管理系统_基于Java的教务管理系统
java项目----教务管理系统_基于Java的教务管理系统 2022-04-22 18:18·java基础 最近为客户开发了一套学校用教务管理系统,主要实现学生.课程.老师.选课等相关的信息化管理功 ...
- 哈夫曼树(Huffman Tree),与哈夫曼编码
目录 一.哈夫曼树 1.什么是哈夫曼树? 2.哈夫曼树关键字说明 3.用代码实现哈夫曼树思路分析 4.代码实现 二.哈夫曼编码 1.哈夫曼编码基本介绍 2.原理剖析 3.代码实现 一.哈夫曼树 1.什 ...
- 数据结构C#版笔记--啥夫曼树(Huffman Tree)与啥夫曼编码(Huffman Encoding)
哈夫曼树Huffman tree 又称最优完全二叉树,切入正题之前,先看几个定义 1.路径 Path 简单点讲,路径就是从一个指定节点走到另一个指定节点所经过的分支,比如下图中的红色分支(A-> ...
- 哈夫曼树(Huffman Tree)
定义 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树.所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数).树的路 ...
- 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码
超详细讲解哈夫曼树(Huffman Tree)以及哈夫曼编码的构造原理.方法,并用代码实现. 1哈夫曼树基本概念 路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径. 结点的路径长度:两 ...
- java是什么格式_是java格式
错误:编码GBK的不可映射字符的解决办法 最近在重新补javaSE的基础,编辑器编写完代码以后,在控制台运行代码段的时候,出现了以下的错误提示:错误:编码GBK的不可映射字符 在通过查询谷哥和度娘以后 ...
- java ee开发环境_设置Java EE 6开发环境
java ee开发环境 本教程简要说明了如何设置典型的环境来开发基于Java EE 6的应用程序. 除了可以正常工作的Windows XP客户端具有足够的CPU能力和内存外,本教程没有其他先决条件. ...
- JAVA redis缓存过期_失效java服务器
Java 并发/多线程教程--4并发模型 本系列译自jakob jenkov的Java并发多线程教程(本章节部分内容参考http://ifeve.com/并发编程模型),个人觉得很有收获.由于个人水平 ...
- java注解的开发_使用Java注解开发自动生成SQL
使用注解开发的好处就是减少配置文件的使用.在实际过程中,随着项目越来越复杂,功能越来越多,会产生非常多的配置文件.但是,当配置文件过多,实际维护过程中产生的问题就不容易定位,这样就会徒劳的增加工作量. ...
最新文章
- dispatch_queue_create(com.biostime.xxx, DISPATCH_QUEUE_SERIAL)的陷阱
- hfss13.0安装教程
- 一维卷积神经网络_序列特征的处理方法之二:基于卷积神经网络方法
- 陈天奇的tvm更新了:VTA,开源AI芯片栈
- matplotlib plot 分组_Python数据分析模块二:Matplotlib
- 创业者怎样才能赚到钱?八种最有效创业赢利模式
- python读取一个文件夹下所有图片_初学Python-找出文件夹下的所有图片
- C#入门4——税收计算
- java多线程学习笔记。
- iPhone/iPad应用使用麦克风的开启和关闭
- android qq很多压缩包,微信QQ总是占用手机大量内存?这次腾讯推出官方版清理工具了...
- 【经典】双子男与天蝎女的爱情故事
- java 23种设计模式详解
- Python的wheel文件安装
- 我们建议您在Google的sellers.json文件中公开您的卖方信息
- 散列函数和数字签名概念
- android 六棱形分析图,[转载]菱形整理形态、三角形整理形态图解及分析
- python 三维坐标图
- Debian下载镜像地址
- java 串口 dtr rts_串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解
热门文章
- php 属性名字访问,php – 如何使用连字符的名称访问此对象属性?
- ubuntu18.04 中个性化配置vim方法
- Syntax Error: Unexpected token 报错原因
- 自己写的_top、_parent以及对iframe和frameset的理解
- echo 1+2+3+4+5“输出的结果是6
- 建议检察院服务器服务器配置 显示器,切换器 键鼠
- 将WindowsPhoneApp部署到HTC Surround,兄弟们支个招如何进行Debug
- 基于Active Directory的用户验证
- 52 - 算法 - LeetCode 20 数据结构类 stack
- java 进阶 知乎_(二)零基础写Java知乎爬虫之进阶篇