霍夫曼编码是一种无损数据压缩算法。在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。例如,在英文中,e的出现机率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个比特。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。*/
构建霍夫曼编码主要包括两个部分:
1)根据输入的字符串构建霍夫曼树。
2)便利霍夫曼数并给每个字符分配编码。
哈夫曼树(Huffman Tree),又叫最优二叉树,指的是对于一组具有确定权值的叶子结点的具有最小带权路径长度的二叉树。
(1)路劲(Path):从树中的一个结点到另一个结点之间的分支构成两个结点间的路径。
(2)路径长度(Path Length):路径上的分支树。
(3)树的路径长度(Path Length of Tree):从树的根结点到每个结点的路径长度之和。在结点数目相同的二叉树中,完全二叉树的路径长度最短。
(4)结点的权(Weight of Node):在一些应用中,赋予树中结点的一个有实际意义的树。
(5)结点的带权路径长度(Weight Path Length of Node):从该结点到树的根结点的路径长度与该结点的权的乘积。
(6)树的带权路径长度(WPL):树中所有叶子结点的带权路径长度之和
构建霍夫曼树的步骤:
算法:输入是没有相同元素的字符数组(长度n)以及字符出现的频率,输出是哈夫曼树。
即假设有n个字符,则构造出得哈夫曼树有n个叶子结点。n个字符的权值(频率)分别设为w1,w2,…,wn,则哈夫曼树的构造规则为:
(1)将w1,w2,…,wn看成是有n棵树的森林(每棵树仅有一个结点);
(2)在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
具体实例及实现代码如下所示:
/***@Title:Huffman.java*@Packagegreedyalgorithm*@Description:TODO*@authorpeidong*@date2017-6-1 上午8:55:59*@versionV1.0*/packagegreedyalgorithm;importjava.util.ArrayList;/***@ClassName:Huffman*@Description:贪心算法之哈夫曼编码*@date2017-6-1 上午8:55:59**//****@ClassName:HuffmanNpde*@Description:构建Huffman树结点*@date2017-6-1 上午8:59:06**/classHuffmanNode{
privateHuffmanNode left;//左子树结点privateHuffmanNode right;//右子树结点privateString data;//结点包含的数据,本例使用字符private floatkey;//待查找的值/****Title:*Description:构造函数*@paramleft*@paramright*@paramdata*@paramkey*/publicHuffmanNode(HuffmanNode left,HuffmanNode right,String data, floatkey){
super();this.left= left;this.right= right;this.data= data;this.key= key;}
/***@returnthe left*/publicHuffmanNode getLeft() {
returnleft;}
/***@paramleftthe left to set*/public voidsetLeft(HuffmanNode left) {
this.left= left;}
/***@returnthe right*/publicHuffmanNode getRight() {
returnright;}
/***@paramrightthe right to set*/public voidsetRight(HuffmanNode right) {
this.right= right;}
/***@returnthe data*/publicString getData() {
returndata;}
/***@paramdatathe data to set*/public voidsetData(String data) {
this.data= data;}
/***@returnthe key*/public floatgetKey() {
returnkey;}
/***@paramkeythe key to set*/public voidsetKey(floatkey) {
this.key= key;}
}
public classHuffman {
/****@Title:getHuffmanCodeNode*@Description:获取huffman树结构*@paramlist*@return*@returnHuffmanNode*@throws*/public staticHuffmanNode getHuffmanCodeNode(ArrayListlist){
while(list.size() >= 2){
//当结点树不止一个时,对元素进行排序sortNodeListByKey(list);//合并key值最小的两个结点HuffmanNode newNode = combine2SmallestNode(list.get(0),list.get(1));list.remove(0);list.remove(0);//ArrayList中remove元素时索引移动list.add(0,newNode);//将合并后的结点加入到原队列}
returnlist.get(0);}
/****@Title:sortNodeListByKey*@Description:按key值单调递增排序结点队列*@paramlist*@returnvoid*@throws*/public static voidsortNodeListByKey(ArrayListlist){
for(inti = 0;i < list.size();i++){
for(intj = i+1;j < list.size();j++){
if(list.get(i).getKey() > list.get(j).getKey()){
//队列中交换位置list.add(i,list.get(j));list.remove(j+1);list.add(j,list.get(i+1));list.remove(i+1);}
}
}
}
/****@Title:combine2SmallestNode*@Description:合并最小的结点*@paramleft*@paramright*@return*@returnHuffmanNode*@throws*/public staticHuffmanNode combine2SmallestNode(HuffmanNode left,HuffmanNode right){
HuffmanNode parentNode = newHuffmanNode(left,right,left.getData() + right.getData(),left.getKey() + right.getKey());returnparentNode;}
/****@Title:printHuffmanNode*@Description:打印结点(前序)*@paramnode*@returnvoid*@throws*/public static voidprintHuffmanNode(HuffmanNode node){
System.out.println("结点:"+ node.getData() + ":"+ "权值:"+ node.getKey());if(node.getLeft() != null){
printHuffmanNode(node.getLeft());}
if(node.getRight() != null){
printHuffmanNode(node.getRight());}
}
/***@Title:main*@Description:测试Huffman程序*@paramargs*@returnvoid*@throws*/public static voidmain(String[] args) {
//TODO Auto-generated method stubArrayListlist = newArrayList();list.add(newHuffmanNode(null, null,"A",0.3f));list.add(newHuffmanNode(null, null,"B",0.1f));list.add(newHuffmanNode(null, null,"C",0.35f));list.add(newHuffmanNode(null, null,"D",0.05f));list.add(newHuffmanNode(null, null,"E",0.2f));printHuffmanNode(getHuffmanCodeNode(list));}
}
//编码后,存储ABCDE的尺寸为:3*0.05+3*0.1+2*0.2+2*0.3+2*0.2=1.85