【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)
赫夫曼树的概念
二叉树结点的度
扩充二叉树

赫夫曼树的外结点和内结点
带权路径长度WPL


赫夫曼树(最优二叉树)


赫夫曼树的构建

Node类的设计
/**
* @Author: HuWan Peng
* @Date Created in 23:21 2018/1/14
*/
public class Node {
char data; // 数据
int weight; // 权值
int left, right, parent; // 三条链接
public Node (char data, int weight) {
this.data = data;
this.weight = weight;
}
public Node (int weight) {
this.weight = weight;
}
}
buildTree方法的设计





具体代码
/**
* @description: 构建赫夫曼树
*/
public Node[] buildTree (Node [] nodes) {
int s1, s2,p;
int n = nodes.length; // 外结点的数量
int m = 2*n - 1; // 内结点 + 外结点的总数量
Node [] HT = new Node [m]; // 存储结点对象的HT数组
for (int i=0;i<m;i++) HT[i] = new Node(0); // 初始化HT数组元素
for (int i=0;i<n;i++) {
HT[i].data = nodes[i].data;
HT[i].weight = nodes[i].weight; //将给定的权值列表赋给外结点对象
}
for (int i=n;i<m;i++) {
s1 = select(HT,i,0); // 取得HT数组中权值最小的结点对象的下标
s2 = select(HT,i,1); // 取得HT数组中权值次小的结点对象的下标
HT[i].left = s1; // 建立链接
HT[i].right = s2;
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].weight = HT[s1].weight + HT[s2].weight;// 计算当前外结点的权值
selectStart+=2; // 这个变量表示之前“被删除”的最小结点的数量和
}
return HT; // 将处理后的HT数组返回
}
/**
* @description: buildTree方法的用例
*/
public static void main (String [] args) {
Node [] nodes = new Node[4];
nodes[0] = new Node('a',7);
nodes[1] = new Node('b',5);
nodes[2] = new Node('c',2);
nodes[3] = new Node('d',4);
HuffmanTree ht = new HuffmanTree();
Node [] n = ht.buildTree(nodes); // n是构建完毕的赫夫曼树
}
}
select方法的设计
private int select (Node[] HT,int range, int rank)
s1 = select(HT,i,0); // 取得HT数组中权值最小的结点对象的下标
s2 = select(HT,i,1); // 取得HT数组中权值次小的结点对象的下标

/**
* @description: 返回权值排名为rank的结点对象在HT中的下标(按权值从小到大排)
*/
private int select (Node[] HT,int range, int rank) {
Node [] copyNodes = Arrays.copyOf(HT, range);// 将HT[0]~HT[range]拷贝到copyNodes中
QuickSort.sort(copyNodes); // 对copyNodes进行从小到大的快速排序
Node target = copyNodes[rank + selectStart]; // 取得“删除”后权值排名为rank的结点对象
for (int j=0;j<HT.length;j++) {
if (target == HT[j]) return j; // 返回该结点对象在数组HT中的下标
}
return -1;
}

赫夫曼树的应用
等长编码和不等长编码

前缀编码
赫夫曼编码的作用


实现赫夫曼编码(encode)

- 如果P.left==X在HT中的下标,则说明X是P的左分支,说明经过的是 0
- 如果P.right==X在HT中的下标,则说明X是P的右分支,说明经过的是 1

import java.util.Arrays;
/**
* @Author: HuWan Peng
* @Date Created in 22:54 2018/1/14
*/
public class HuffmanTree {
private class HuffmanCode {
char data; // 存放字符,例如 'C'
String bit; // 存放编码后的字符串, 例如"111"
public HuffmanCode (char data, String bit) {
this.data = data;
this.bit = bit;
}
}
/**
* @description: 构建赫夫曼树
*/
public Node[] buildTree (Node [] nodes) {
// 具体代码见上文
}
/**
* @description: 进行赫夫曼编码
*/
public HuffmanCode [] encode(Node [] nodes) {
Node [] HT = buildTree(nodes); // 根据输入的nodes数组构造赫夫曼树
int n = nodes.length;
HuffmanCode [] HC = new HuffmanCode [n];
String bit;
for (int i=0;i<n;i++) { // 遍历各个叶子结点
bit = "";
for (int c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent) { // 从叶子结点上溯到根结点
if(HT[f].left == c) bit= "0" + bit; // 反向编码
else bit= "1" + bit;
}
HC[i] = new HuffmanCode(HT[i].data,bit); // 将字符和对应的编码存储起来
}
return HC;
}
/**
* @description: encode方法的用例
*/
public static void main (String [] args) {
Node [] nodes = new Node[4];
nodes[0] = new Node('A',7);
nodes[1] = new Node('B',5);
nodes[2] = new Node('C',2);
nodes[3] = new Node('D',4);
HuffmanTree ht = new HuffmanTree();
HuffmanCode[] hc = ht.encode(nodes);
// 对A,B,C,D进行编码
for (int i=0;i<hc.length;i++) { // 将赫夫曼编码打印出来
System.out.println(hc[i].data + ":" +hc[i].bit);
}
}
}
A:0
B:10
C:110
D:111
赫夫曼译码(decode)
import java.util.Arrays;
/**
* @Author: HuWan Peng
* @Date Created in 22:54 2018/1/14
*/
public class HuffmanTree {
int selectStart = 0;
private class HuffmanCode {
char data; // 存放字符,例如 'C'
String bit; // 存放编码后的字符串, 例如"111"
public HuffmanCode (char data, String bit) {
this.data = data;
this.bit = bit;
}
}
/**
* @description: 构建赫夫曼树
*/
public Node[] buildTree (Node [] nodes) {
// 代码见上文
}
/**
* @description: 进行赫夫曼译码
*/
public String decode (Node [] nodes, String code) {
String str="";
Node [] HT = buildTree(nodes);
int n =HT.length -1;
for (int i=0;i<code.length();i++) {
char c = code.charAt(i);
if(c == '1') {
n = HT[n].right;
}
else {
n = HT[n].left;
}
if(HT[n].left == 0) {
str+= HT[n].data;
n =HT.length -1;
}
}
return str;
}
/**
* @description: decode方法的用例
*/
public static void main (String [] args) {
Node [] nodes = new Node[4];
nodes[0] = new Node('A',7);
nodes[1] = new Node('B',5);
nodes[2] = new Node('C',2);
nodes[3] = new Node('D',4);
HuffmanTree ht = new HuffmanTree();
// 对 010110111 进行译码
System.out.println(ht.decode(nodes,"010110111"));
}
}
ABCD
全部代码:
Node.java
/**
* @Author: HuWan Peng
* @Date Created in 23:21 2018/1/14
*/
public class Node {
char data;
int weight;
int left, right, parent;
public Node (char data, int weight) {
this.data = data;
this.weight = weight;
}
public Node (int weight) {
this.weight = weight;
}
}
HuffmanTree.java
import java.util.Arrays;
/**
* @Author: HuWan Peng
* @Date Created in 22:54 2018/1/14
*/
public class HuffmanTree {
int selectStart = 0;
private class HuffmanCode {
char data; // 存放字符,例如 'C'
String bit; // 存放编码后的字符串, 例如"111"
public HuffmanCode (char data, String bit) {
this.data = data;
this.bit = bit;
}
}
/**
* @description: 返回权值排名为rank的结点对象在nodes中的下标(按权值从小到大排)
*/
private int select (Node[] HT,int range, int rank) {
Node [] copyNodes = Arrays.copyOf(HT, range);// 将HT[0]~HT[range]拷贝到copyNodes中
QuickSort.sort(copyNodes); // 对copyNodes进行从小到大的快速排序
Node target = copyNodes[rank + selectStart]; // 取得“删除”后权值排名为rank的结点对象
for (int j=0;j<HT.length;j++) {
if (target == HT[j]) return j; // 返回该结点对象在数组HT中的下标
}
return -1;
}
/**
* @description: 构建赫夫曼树
*/
public Node[] buildTree (Node [] nodes) {
int s1, s2,p;
int n = nodes.length; // 外结点的数量
int m = 2*n - 1; // 内结点 + 外结点的总数量
Node [] HT = new Node [m]; // 存储结点对象的HT数组
for (int i=0;i<m;i++) HT[i] = new Node(0); // 初始化HT数组元素
for (int i=0;i<n;i++) {
HT[i].data = nodes[i].data;
HT[i].weight = nodes[i].weight; //将给定的权值列表赋给外结点对象
}
for (int i=n;i<m;i++) {
s1 = select(HT,i,0); // 取得HT数组中权值最小的结点对象的下标
s2 = select(HT,i,1); // 取得HT数组中权值次小的结点对象的下标
HT[i].left = s1; // 建立链接
HT[i].right = s2;
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].weight = HT[s1].weight + HT[s2].weight;// 计算当前外结点的权值
selectStart+=2; // 这个变量表示之前“被删除”的最小结点的数量和
}
return HT; // 将处理后的HT数组返回
}
/**
* @description: 进行赫夫曼编码
*/
public HuffmanCode [] encode(Node [] nodes) {
Node [] HT = buildTree(nodes); // 根据输入的nodes数组构造赫夫曼树
int n = nodes.length;
HuffmanCode [] HC = new HuffmanCode [n];
String bit;
for (int i=0;i<n;i++) { // 遍历各个叶子结点
bit = "";
for (int c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent) { // 从叶子结点上溯到根结点
if(HT[f].left == c) bit= "0" + bit; // 反向编码
else bit= "1" + bit;
}
HC[i] = new HuffmanCode(HT[i].data,bit); // 将字符和对应的编码存储起来
}
return HC;
}
/**
* @description: 进行赫夫曼译码
*/
public String decode (Node [] nodes, String code) {
String str="";
Node [] HT = buildTree(nodes);
int n =HT.length -1;
for (int i=0;i<code.length();i++) {
char c = code.charAt(i);
if(c == '1') {
n = HT[n].right;
}
else {
n = HT[n].left;
}
if(HT[n].left == 0) {
str+= HT[n].data;
n =HT.length -1;
}
}
return str;
}
/**
* @description: buildTree方法的用例
*/
public static void main (String [] args) {
Node [] nodes = new Node[4];
nodes[0] = new Node('A',7);
nodes[1] = new Node('B',5);
nodes[2] = new Node('C',2);
nodes[3] = new Node('D',4);
HuffmanTree ht = new HuffmanTree();
System.out.println(ht.decode(nodes,"010110111"));
}
}
QuickSort.java
/**
* @Author: HuWan Peng
* @Date Created in 22:56 2018/1/14
*/
public class QuickSort {
/**
* @description: 交换两个数组元素
*/
private static void exchange(Node [] a , int i, int j) {
Node temp = a[i];
a[i] = a[j];
a[j] = temp;
}
/**
* @description: 切分函数
*/
private static int partition (Node [] a, int low, int high) {
int i = low, j = high+1; // i, j为左右扫描指针
int pivotkey = a[low].weight; // pivotkey 为选取的基准元素(头元素)
while(true) {
while (a[--j].weight>pivotkey) { if(j == low) break; } // 右游标左移
while(a[++i].weight<pivotkey) { if(i == high) break; } // 左游标右移
if(i>=j) break; // 左右游标相遇时候停止, 所以跳出外部while循环
else exchange(a,i, j) ; // 左右游标未相遇时停止, 交换各自所指元素,循环继续
}
exchange(a, low, j); // 基准元素和游标相遇时所指元素交换,为最后一次交换
return j; // 一趟排序完成, 返回基准元素位置
}
/**
* @description: 根据给定的权值对数组进行排序
*/
private static void sort (Node [] a, int low, int high) {
if(high<= low) { return; } // 当high == low, 此时已是单元素子数组,自然有序, 故终止递归
int j = partition(a, low, high); // 调用partition进行切分
sort(a, low, j-1); // 对上一轮排序(切分)时,基准元素左边的子数组进行递归
sort(a, j+1, high); // 对上一轮排序(切分)时,基准元素右边的子数组进行递归
}
public static void sort (Node [] a){ //sort函数重载, 只向外暴露一个数组参数
sort(a, 0, a.length-1);
}
}

【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)的更多相关文章
- Android版数据结构与算法(七):赫夫曼树
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 近期忙着新版本的开发,此外正在回顾C语言,大部分时间没放在数据结构与算法的整理上,所以更新有点慢了,不过既然写了就肯定尽力将这部分完全整理好分享出 ...
- C#数据结构-赫夫曼树
什么是赫夫曼树? 赫夫曼树(Huffman Tree)是指给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小.哈夫曼树(也称为最优二叉树)是带权路径长度最短的树,权值较大的结点 ...
- Java数据结构和算法(四)赫夫曼树
Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...
- 【数据结构】赫夫曼树的实现和模拟压缩(C++)
赫夫曼(Huffman)树,由发明它的人物命名,又称最优树,是一类带权路径最短的二叉树,主要用于数据压缩传输. 赫夫曼树的构造过程相对比较简单,要理解赫夫曼数,要先了解赫夫曼编码. 对一组出现频率不同 ...
- 赫夫曼树JAVA实现及分析
一,介绍 1)构造赫夫曼树的算法是一个贪心算法,贪心的地方在于:总是选取当前频率(权值)最低的两个结点来进行合并,构造新结点. 2)使用最小堆来选取频率最小的节点,有助于提高算法效率,因为要选频率最低 ...
- javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题
赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支 ...
- puk1521 赫夫曼树编码
Description An entropy encoder is a data encoding method that achieves lossless data compression by ...
- Python---哈夫曼树---Huffman Tree
今天要讲的是天才哈夫曼的哈夫曼编码,这是树形数据结构的一个典型应用. !!!敲黑板!!!哈夫曼树的构建以及编码方式将是我们的学习重点. 老方式,代码+解释,手把手教你Python完成哈夫曼编码的全过程 ...
- 哈夫曼树Huffman
哈夫曼树处理这样的一种问题: 给出一棵n个叶子的k叉树,每个叶子有一个权值wi,要求最小化∑wi*di di表示,第i个叶子节点到根节点的距离.(一般是边数) 处理方法比较固定. 贪心的思路:我们让权 ...
随机推荐
- android 事件传递机制(1)
在项目中,经常遇到事件冲突,ScrollView,ViewPager滑动卡顿等情况,比如:onClick和onLongClick事件冲突,dispatchTouchEvent,onInterceptT ...
- 在windows平台使用Apache James搭建邮件服务器以及使用C#向外网发送邮件
首先环境搭建: 1.下载安装JDK,并且配置环境变量 2.下载Apache James ,下载解压之后的目录如图 双击bin下边的run.bat批处理文件安装James 服务,提示如下信息说明安装成功 ...
- 项目实战12.2—企业级监控工具应用实战-zabbix操作进阶
无监控,不运维.好了,废话不多说,下面都是干货. 流量党勿入,图片太多!!! 项目实战系列,总架构图 http://www.cnblogs.com/along21/p/8000812.html 一.U ...
- MyBatis_查询缓存01
一.查询缓存 查询缓存的使用,主要是为了提高查询访问速度.将用户对同一数据的重复查询过程简单化,不在每次均从数据库中查询获取结果数据,从而提高访问速度. MyBatis的查询缓存机制,根据缓存区的作用 ...
- JavaScript 数组最大值
JavaScript 数组最大值 在js中可以使用Math.max()获取最大值. 如: console.log(Math.max("1","11"," ...
- 《代码大全(第二版)》【PDF】下载
<代码大全(第二版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382264 内容简介 <代码大全(第2版)>是著 ...
- <大话设计模式>笔记
读完了<大话设计模式>这本书,收获很多,对程序设计有了很多新的理解.将每章模式的大概要点做了些笔记以备查阅,一些设计模式书读完也对其了解得不是很透彻,需要以后在实践中来不断地加深理解吧.读 ...
- Oracle数据库(三)表操作,连接查询,分页
复制表 --复制表 create table new_table as select * from Product --复制表结构不要数据 在where后面跟一个不成立的条件,就会仅复制表的结构而不复 ...
- sourceTree每次拉取代码和提交代码都需要输入密码
今天新安装的sourceTree导入项目,拉取代码的时候一直提示让我输入git密码,每次拉取和提交的时候都需要重新输入密码,甚是麻烦,在网上,搜索,解决办法五花八门,这里提供一种简单有效的方法供大家参 ...
- caffe CuDNN报错问题解决
解决cudnn问题:Loaded runtime CuDNN library: 5005 (compatibility version 5000) but source was compiled wi ...