Huffman树是一种在编码技术方面得到广泛应用的二叉树,它也是一种最优二叉树。

  一、霍夫曼树的基本概念

  1.结点的路径和结点的路径长度:结点间的路径是指从一个结点到另一个结点所经历的结点和分支序列。结点的路径长度是指从根结点到该结点间的路径上的分支数目。

  2.结点的权和结点的带权路径长度:结点的权是指结点被赋予一个具有某种实际意义的数值。结点的带权路径长度是该结点的路径长度与结点的权值的乘积。

  3.树的长度和树的带权路径长度:树的长度就是从根结点到每一结点的路径长度之和。树的带权路径长度就是所有叶结点的带权路径长度之和。

  4.最优二叉树:带权路径长度WPL最小的二叉树称为霍夫曼树(Huffman Tree)或最优二叉树。

  

  二叉树a的WPL = 5 x 1 + 15 x 2 + 40 x 3 + 30 x 4 + 10 x 5 = 315

  二叉树b的WPL = 5 x 3 + 15 x 3 + 40 x 2 + 30 x 2 + 10 x 2 = 220

  二、霍夫曼树的构造方法

  • 由给定的n个权值{w1,w2,... ,wn}构成由n棵二叉树所构成的森林F={T1,T2,...,Tn},其中每棵二叉树只有一个根结点,并且根结点的权值对应于w1,w2,...,wn
  • 在F中选取根结点的权值最小的两棵树,分别把它们作为左子树和右子树去构造一棵新的二叉树,新的二叉树的各结点的权值为左、右子树的权值之和
  • 在F中删除上一步中选中的两棵树,并把新产生的二叉树加入到F中
  • 重复第2步和第3步,直到F只含一棵树为止,这棵树就是霍夫曼树。

  

  三、霍夫曼编码

  霍夫曼树更大的目的是解决了当年远距离通信(电报)的数据传输的最优化问题。

  霍夫曼编码的定义:设需要编码的字符集为(d1,d2,...,dn)各个字符在电文中出现的次数或者频率集合为{w1,w2,...,wn},以d1,d2,...,dn为叶子结点,w1,w2,...,wn作为相应叶子结点的权值来构造一棵霍夫曼树。规定霍夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的路径分支组成的0和1序列边为该结点对应字符的编码,这就是霍夫曼编码。

  假设六个字母的频率为A 27,B 8,C 15, D 15, E 30, F 5,则对应的霍夫曼编码为:A 01, B 1001,C 101,D 00,E 11,F 1000

  

  设计非等长码的时候,必须是任一字符的编码都不是另一个字符的编码的前缀,这种编码称为前缀编码。

  四、构造霍夫曼树和霍夫曼编码的实现思路:

  (1)算法思路

  考虑到,在进行霍夫曼译码时,要求能方便地实现从双亲结点到左、右孩子结点的操作;而在霍夫曼编码时,又要求能够方便地从孩子结点到双亲结点的操作,因此,需要将霍夫曼树的结点存储结构设计为三叉链式(二叉链表结点结构加上双亲parent域(数据域data是weight权值域))存储结构。此外,每一个结点还要设置权值域。为了判断一个结点是否已经加入到霍夫曼树中,每个结点还需要设置一个标志域flag,当flag为0时,表示该结点尚未加入到霍夫曼树中;反之,表示该结点已经加入到霍夫曼树中。  

  综上,霍夫曼树的结点存储结构为:weight   flag   parent  rchild lchild

  (2)其中,最为关键的就是从结点当中选择两个权重最小和权重第二小的结点,算法如下:

    // 在霍夫曼树结点数组的[0],[1]...[end]中选择不在霍夫曼树中且weight最小的两个结点
private HuffmanNode[] selectMinWeight(HuffmanNode[] HN, int end) {
HuffmanNode[] min = {new HuffmanNode(100),new HuffmanNode(100)};
for (int i = 0; i <= end; i++) {
HuffmanNode h = HN[i];
if (h.flag == 0 && h.weight < min[0].weight) {
min[1] = min[0];
min[0] = h;
} else if (h.weight < min[1].weight && h.flag == 0) {
min[1] = h;
}
}
return min;
}

  其中,flag==1说明该结点已经被选入到霍夫曼树当中了。

  分析一下上述算法,为了从结点数组HN当中的下标为[0]...[end]选择权值最小的两个结点。首先建立了一个结点数组用来存储权值最小的两个结点,这两个结点的初始权值设为最大值100,是为了便于比较,然后min[0]作为权重最小的结点,而min[1]最为权重第二小的结点,算法思路是这样的:从数组下标为0开始:

  • 如果新结点的权值小于min[0]的权值,那么将新结点赋值给min[0],并将之前最小的min[0]赋值给之前第二小的min[1]。
  • 如果新结点的权值大于min[0]的权值而小于min[1]的权值,那么最小权值结点还是min[0],而将第二小的min[1]赋值为新结点。

  (3)同时还需要注意的是,在霍夫曼树中求叶结点的霍夫曼编码,实际就是从叶结点到根结点的路径分支的逐个遍历,没经过一个分支就得到一位霍夫曼编码值。因此,霍夫曼编码需要保存在一个整型数组中,并且由于求每一个字符的霍夫曼编码是从叶结点到根结点的一个逆向处理过程,所以对获取到的霍夫曼编码,应该按位从数组的结尾位置开始进行存放。又由于是不等长的编码,所以还需要设置一个标识来表示每个霍夫曼编码在数组中的起始位置。

  以六个字母的频率为A 27,B 8,C 15, D 15, E 30, F 5,则对应的霍夫曼编码为:A 01, B 1001,C 101,D 00,E 11,F 1000,为例

  

  其霍夫曼编码在数组中的存储形式应该是:

  

  代码实现为(遍历的时候是从叶结点到根结点,而读序列的时候是从根结点到叶结点):

        int[][] Huffcode = new int[n][n];        // 建立霍夫曼编码二维数组
for (int j = 0; j < n; j++) { // 一共n个结点
int start = n - 1; // 编码开始的位置,初始化为数组的结尾
//
for (HuffmanNode c = HN[j], p = c.parent ; p != null ; c = p, p = p.parent) {
if (p.lchild.equals(c)) {
Huffcode[j][start--] = 0; // 左孩子编码为0
} else {
Huffcode[j][start--] = 1; // 右孩子编码为1
}
}
Huffcode[j][start] = -1; // 编码的开始标识为-1,即从-1开始后面才是编码序列
}
return Huffcode;

  

  五、构造霍夫曼树和霍夫曼编码的Java语言实现

  • 霍夫曼结点类:
package bigjun.iplab.huffmanTree;
/**
* Huffman Tree的结点存储结构
*/
public class HuffmanNode { public int weight; // 结点权值
public short flag; // 结点是否加入到霍夫曼树的标识
public HuffmanNode parent, lchild, rchild; // 结点的双亲、左孩子和右孩子 public HuffmanNode() { // 构造一个空结点
this(0);
} public HuffmanNode(int weight) { // 构造一个非空结点
this.weight = weight;
flag = 0;
parent = lchild = rchild = null;
} }
  • 霍夫曼树及霍夫曼编码实现类:
package bigjun.iplab.huffmanTree;

public class HuffmanTree {

    public int[][] huffmanCoding(int[] W){
int n = W.length; // 权重数组的长度
int m = 2 * n - 1; // 霍夫曼树结点的总个数
HuffmanNode[] HN = new HuffmanNode[m]; // 霍夫曼树的结点数组
int i; // 霍夫曼树的结点数组的下标
for (i = 0; i < n; i++) { // 构造n个具有给定权值的结点,放在结点数组的前n个位置
HN[i] = new HuffmanNode(W[i]);
}
for (i = n; i < m; i++) { // 从数组下标n开始,存放其他的结点
HuffmanNode[] minNode = selectMinWeight(HN, i - 1); // 从结点数组中的[0],[1]...[i-1]中选择权值最小的一个
HuffmanNode min1 = minNode[0]; // 选出数组结点中权值最小的结点
HuffmanNode min2 = minNode[1]; // 选出数组结点中权值第二小的结点 min1.flag = 1; // 标记已经被选中到结点数组中
min2.flag = 1;
HN[i] = new HuffmanNode(); // 新建霍夫曼结点,放在数组下标为i的位置
min1.parent = HN[i]; // 权值最小结点和第二小结点的双亲为新结点
min2.parent = HN[i];
HN[i].lchild = min1; // 新结点的左、右孩子为权值最小的两个结点
HN[i].rchild = min2;
HN[i].weight = min1.weight + min2.weight;// 新结点的权重就是两个孩子的权值之和
} int[][] Huffcode = new int[n][n]; // 建立霍夫曼编码二维数组
for (int j = 0; j < n; j++) { // 一共n个结点
int start = n - 1; // 编码开始的位置,初始化为数组的结尾
//
for (HuffmanNode c = HN[j], p = c.parent ; p != null ; c = p, p = p.parent) {
if (p.lchild.equals(c)) {
Huffcode[j][start--] = 0; // 左孩子编码为0
} else {
Huffcode[j][start--] = 1; // 右孩子编码为1
}
}
Huffcode[j][start] = -1; // 编码的开始标识为-1,即从-1开始后面才是编码序列
}
return Huffcode; } // 在霍夫曼树结点数组的[0],[1]...[end]中选择不在霍夫曼树中且weight最小的两个结点
private HuffmanNode[] selectMinWeight(HuffmanNode[] HN, int end) {
HuffmanNode[] min = {new HuffmanNode(100),new HuffmanNode(100)};
for (int i = 0; i <= end; i++) {
HuffmanNode h = HN[i];
if (h.flag == 0 && h.weight < min[0].weight) {
min[1] = min[0];
min[0] = h;
} else if (h.weight < min[1].weight && h.flag == 0) {
min[1] = h;
}
}
return min;
} public static void main(String[] args) {
int[] W = {27, 8, 15, 15, 30, 5};
HuffmanTree hTree = new HuffmanTree();
int[][] HN = hTree.huffmanCoding(W);
for (int i = 0; i < HN.length; i++) {
System.out.print(W[i] + "的霍夫曼编码为 ");
for (int j = 0; j < HN[i].length; j++) {
if (HN[i][j] == -1) {
for (int k = j + 1; k < HN[i].length; k++) {
System.out.print(HN[i][k]);
}
break;
}
}
System.out.println();
}
}
}
  • 输出:
27的霍夫曼编码为 01
8的霍夫曼编码为 1001
15的霍夫曼编码为 101
15的霍夫曼编码为 00
30的霍夫曼编码为 11
5的霍夫曼编码为 1000

数据结构(二十七)Huffman树和Huffman编码的更多相关文章

  1. huffman树即Huffma编码的实现

    自己写的Huffman树生成与Huffman编码实现 (实现了核心功能 ,打出了每个字符的huffman编码 其他的懒得实现了,有兴趣的朋友可以自己在我的基础增加功能 ) /* 原创文章 转载请附上原 ...

  2. Huffman树与Huffman编码

    1.Huffman树 今天复习Huffman树.依稀记得自己被Huffman树虐的经历.还记得是7月份,我刚开始看数据结构与算法,根本看不懂Huffman树的操作.后来我终于悟出了Huffman树是怎 ...

  3. 数据结构与算法(周鹏-未出版)-第六章 树-6.5 Huffman 树

    6.5 Huffman 树 Huffman 树又称最优树,可以用来构造最优编码,用于信息传输.数据压缩等方面,是一类有着广泛应用的二叉树. 6.5.1 二叉编码树 在计算机系统中,符号数据在处理之前首 ...

  4. Huffman树的构造及编码与译码的实现

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

  5. Huffman树的编码译码

    上个学期做的课程设计,关于Huffman树的编码译码. 要求: 输入Huffman树各个叶结点的字符和权值,建立Huffman树并执行编码操作 输入一行仅由01组成的电文字符串,根据建立的Huffma ...

  6. Huffman树与编码

    带权路径最小的二叉树称为最优二叉树或Huffman(哈夫曼树). Huffman树的构造 将节点的权值存入数组中,由数组开始构造Huffman树.初始化指针数组,指针指向含有权值的孤立节点. b = ...

  7. Huffman树及其编解码

    Huffman树--编解码 介绍:   Huffman树可以根据输入的字符串中某个字符出现的次数来给某个字符设定一个权值,然后可以根据权值的大小给一个给定的字符串编码,或者对一串编码进行解码,可以用于 ...

  8. POJ 2155 Matrix (二维线段树)

    Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 17226   Accepted: 6461 Descripti ...

  9. HDU 4819 Mosaic (二维线段树)

    Mosaic Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)Total S ...

随机推荐

  1. Graphlab create的基本使用

    写在前面 GraphLab Create 是一款机器学习的函数库,其中的SFrame也是十分强大的数据管理工具.它允许直接从硬盘中读取数据,免于将数据全部加载到内存中.这就使得对于大数据的处理成为可能 ...

  2. Hibernate 中setResultTransformer使用

    在使用hibernate框架,查询数据库多张表或者单张表的某几个需要的字段数据时,往往只能通过sql语句配合setResultTransformer将查询到的数据封装到一个map集合中,再将map集合 ...

  3. 【SQL server基础】初步学习存储过程(好学易懂)

    -------------------------------------------------------------------------- ------------------------- ...

  4. 检测MySQL主从是否异常

    #!bin/bash user='root' passwd="123" host="192.168.192.156" mycmd="mysql -u$ ...

  5. 使用 Jenkins 与 Sonar 集成对代码进行持续检测

    SonarQube 与 Jenkins 简介 SonarQube是 一个开源的代码质量分析平台,便于管理代码的质量,可检查出项目代码的漏洞和潜在的逻辑问题.同时,它提供了丰富的插件,支持多种语言的检测 ...

  6. Redis分片机制

    文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. 前两篇文章对Redis主从复制和主从切换的知识点进行了介绍,但是也很明显的有一点小弊端: 需要定时进行主从复制 ...

  7. 数据表管理admin

    知识预览 admin组件使用 admin源码解析 回到顶部 admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你 ...

  8. day 19作业

    目录 今日作业: 今日作业: 1.什么是对象?什么是类? 答:对象是特征与技能的集合体,类是一系列对象相同的特征与技能的结合体 2.绑定方法的有什么特点 答:由对象来调用称之为对象的绑定方法,不同的对 ...

  9. dubbo配置文件的加载顺序详解(图示)

    Dubbo配置文件的加载顺序 ​ 在使用apache dubbo.version2.7.3 时,配置文件的加载情况.以provider提供服务者为例. 配置文件 ,以下四个配置文件. 其优先级 app ...

  10. 2019头条java面试总结 (包含面试题解析)

    2019滴滴java面试总结  (包含面试题) 本人8年开发经验.今年年初找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.滴滴等公司offer,岗位是Java后端开发. 面试了很多家公司,感觉大部分 ...