背景
由于目前常用的图像、音频等多媒体的信息量巨大,因此必须采用数据压缩技术来存储和传输。数据压缩技术通过对数据进行重新编码来压缩存储,以便减少数据占用的存储空间,在使用时再进行解压缩,恢复数据的原有特性。
压缩方法主要由有损压缩和无损压缩。有损压缩是指压缩过程中可能会丢失数据信息;无损压缩是指压缩存储数据的全部信息,保证解压后的数据不丢失。哈夫曼编码是一种无损压缩技术。
基本概念
- 结点间路径:从一个结点到另一个结点所经过的结点序列
- 结点路径长度:从根结点到结点的路径上的边数
- 结点的权值:人为赋予结点的一个具有某种实际意义的值(例:可以为某个数出现的频数等)
- 结点的带权路径长度:结点的权值和结点的路径长度的乘积
- 树的带权路径长度:树的叶节点的带权路径长度之和(WPL)
- 最优二叉树:指n个带有权值的叶子结点作为二叉树, 构造出的具有最小带权路径长度的二叉树,即哈弗曼树
特点
- 权值大的,路径短,权值小的,路径大;
- 只有度为0(叶子结点)和度为2(分支结点)的结点,不存在度为1的结点
核心思想
由给定的n个结点作为根结点构成森林,依次选取其中权值最小的两个根结点作为左右子结点,并向上合并构成其父节点。将该父节点加入森林,并同时删除已选中的两个子结点。重复抽取,直到森林中只有一颗树为止。
- 初始化:由给定n个具有确定权值的树构成森林,森林中的每个树只有根结点,即{w1,w2,w3,…,wn},其中wi为权值
- 选取与合并:从森林中选取权值最小的两个树,分别作为左右子树从而构成一个新的树,新树的根结点权值为这两个左右子树的权值之和
- 添加与删除:从森林中删除选出的两个树,同时加入新生成的树
- 重复:继续循环(2)(3)步骤,知道只剩一个树为止,即为哈弗曼树
存储结构
- 设置长度为2n-1的数组(n为森林中根结点个数,最多具有2n-1个结点)
- 以链表结构构造哈夫曼结点
实现代码
public class HuffmanTree {
private class HuffmanNode{
public int weight;//哈夫曼结点的权重
public boolean flag;//标记是否已经进入哈弗曼树
public HuffmanNode lchild,rchild,parent;//保存当前节点的左右孩子结点及双亲结点
HuffmanNode(){
this(0);
}
HuffmanNode(int weight){
this.weight = weight;
flag = false;
lchild = rchild = parent = null;
}
}
/**
* 根据根结点权值数组构建哈夫曼树
* @param w 所有结点的权值构成的数组
*/
public HuffmanTree(int[] w) {
int l = w.length;
int n = 2 * l - 1;// 哈夫曼结点数
HuffmanNode[] node = new HuffmanNode[n];
for(int i=0;i<n;i++)
node[i] = new HuffmanNode(w[i]);//初始化森林
for(int i=l;i<n;i++) {
HuffmanNode m1 = selectMin(node,i);
m1.flag = true;
HuffmanNode m2 = selectMin(node,i);
m2.flag = true;
node[i].lchild = m1;
node[i].rchild = m2;
node[i].weight = m1.weight + m2.weight;
m1.parent = node[i];
m2.parent = node[i];
}
}
/**
* 从哈夫曼结点数组中选取权值最小的结点
* @param node 保存哈夫曼结点的数组
* @param len 数组的长度
* @return
*/
private HuffmanNode selectMin(HuffmanNode[] node, int len) {
int min = 0;
for(int i=1;i<len;i++) {
if(!node[i].flag&&(node[i].weight<node[min].weight))
min = i;
}
return node[min];
}
/**
* 构建哈夫曼编码
* @param node 哈夫曼结点数组
* @param n 数组长度
* @return
*/
public int[][] HuffmanCode(HuffmanNode[] node, int n){
int[][] HuffmanCode = new int[n][n];
for(int i=0;i<n;i++) {
int k = n-1;
HuffmanNode t = node[i],p = t.parent;
for(;p!=null;p=p.parent) {
if(p.lchild == t)
HuffmanCode[i][k--] = 0;
else
HuffmanCode[i][k--] = 1;
}
}
return HuffmanCode;
}
}