-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)相关推荐

  1. java项目----教务管理系统_基于Java的教务管理系统

    java项目----教务管理系统_基于Java的教务管理系统 2022-04-22 18:18·java基础 最近为客户开发了一套学校用教务管理系统,主要实现学生.课程.老师.选课等相关的信息化管理功 ...

  2. 哈夫曼树(Huffman Tree),与哈夫曼编码

    目录 一.哈夫曼树 1.什么是哈夫曼树? 2.哈夫曼树关键字说明 3.用代码实现哈夫曼树思路分析 4.代码实现 二.哈夫曼编码 1.哈夫曼编码基本介绍 2.原理剖析 3.代码实现 一.哈夫曼树 1.什 ...

  3. 数据结构C#版笔记--啥夫曼树(Huffman Tree)与啥夫曼编码(Huffman Encoding)

    哈夫曼树Huffman tree 又称最优完全二叉树,切入正题之前,先看几个定义 1.路径 Path 简单点讲,路径就是从一个指定节点走到另一个指定节点所经过的分支,比如下图中的红色分支(A-> ...

  4. 哈夫曼树(Huffman Tree)

    定义 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树.所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数).树的路 ...

  5. 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码

    超详细讲解哈夫曼树(Huffman Tree)以及哈夫曼编码的构造原理.方法,并用代码实现. 1哈夫曼树基本概念 路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径. 结点的路径长度:两 ...

  6. java是什么格式_是java格式

    错误:编码GBK的不可映射字符的解决办法 最近在重新补javaSE的基础,编辑器编写完代码以后,在控制台运行代码段的时候,出现了以下的错误提示:错误:编码GBK的不可映射字符 在通过查询谷哥和度娘以后 ...

  7. java ee开发环境_设置Java EE 6开发环境

    java ee开发环境 本教程简要说明了如何设置典型的环境来开发基于Java EE 6的应用程序. 除了可以正常工作的Windows XP客户端具有足够的CPU能力和内存外,本教程没有其他先决条件. ...

  8. JAVA redis缓存过期_失效java服务器

    Java 并发/多线程教程--4并发模型 本系列译自jakob jenkov的Java并发多线程教程(本章节部分内容参考http://ifeve.com/并发编程模型),个人觉得很有收获.由于个人水平 ...

  9. java注解的开发_使用Java注解开发自动生成SQL

    使用注解开发的好处就是减少配置文件的使用.在实际过程中,随着项目越来越复杂,功能越来越多,会产生非常多的配置文件.但是,当配置文件过多,实际维护过程中产生的问题就不容易定位,这样就会徒劳的增加工作量. ...

最新文章

  1. dispatch_queue_create(com.biostime.xxx, DISPATCH_QUEUE_SERIAL)的陷阱
  2. hfss13.0安装教程
  3. 一维卷积神经网络_序列特征的处理方法之二:基于卷积神经网络方法
  4. 陈天奇的tvm更新了:VTA,开源AI芯片栈
  5. matplotlib plot 分组_Python数据分析模块二:Matplotlib
  6. 创业者怎样才能赚到钱?八种最有效创业赢利模式
  7. python读取一个文件夹下所有图片_初学Python-找出文件夹下的所有图片
  8. C#入门4——税收计算
  9. java多线程学习笔记。
  10. iPhone/iPad应用使用麦克风的开启和关闭
  11. android qq很多压缩包,微信QQ总是占用手机大量内存?这次腾讯推出官方版清理工具了...
  12. 【经典】双子男与天蝎女的爱情故事
  13. java 23种设计模式详解
  14. Python的wheel文件安装
  15. 我们建议您在Google的sellers.json文件中公开您的卖方信息
  16. 散列函数和数字签名概念
  17. android 六棱形分析图,[转载]菱形整理形态、三角形整理形态图解及分析
  18. python 三维坐标图
  19. Debian下载镜像地址
  20. java 串口 dtr rts_串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解

热门文章

  1. php 属性名字访问,php – 如何使用连字符的名称访问此对象属性?
  2. ubuntu18.04 中个性化配置vim方法
  3. Syntax Error: Unexpected token 报错原因
  4. 自己写的_top、_parent以及对iframe和frameset的理解
  5. echo 1+2+3+4+5“输出的结果是6
  6. 建议检察院服务器服务器配置 显示器,切换器 键鼠
  7. 将WindowsPhoneApp部署到HTC Surround,兄弟们支个招如何进行Debug
  8. 基于Active Directory的用户验证
  9. 52 - 算法 - LeetCode 20 数据结构类 stack
  10. java 进阶 知乎_(二)零基础写Java知乎爬虫之进阶篇