霍夫曼编码是一种无损数据压缩算法。在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。例如,在英文中,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 * @Package greedyalgorithm * @Description: TODO * @author peidong * @date 2017-6-1 上午8:55:59 * @version V1.0 */ package greedyalgorithm; import java.util.ArrayList; /** * @ClassName: Huffman * @Description: 贪心算法之哈夫曼编码 * @date 2017-6-1 上午8:55:59 * */ /** * * @ClassName: HuffmanNpde * @Description: 构建Huffman树结点 * @date 2017-6-1 上午8:59:06 * */ class HuffmanNode{ private HuffmanNode left; //左子树结点 private HuffmanNode right;//右子树结点 private String data; //结点包含的数据,本例使用字符 private float key; //待查找的值 /** * * <p>Title: </p> * <p>Description:构造函数 </p> * @param left * @param right * @param data * @param key */ public HuffmanNode(HuffmanNode left, HuffmanNode right, String data, float key){ super(); this.left = left; this.right = right; this.data = data; this.key = key; } /** * @return the left */ public HuffmanNode getLeft() { return left; } /** * @param left the left to set */ public void setLeft(HuffmanNode left) { this.left = left; } /** * @return the right */ public HuffmanNode getRight() { return right; } /** * @param right the right to set */ public void setRight(HuffmanNode right) { this.right = right; } /** * @return the data */ public String getData() { return data; } /** * @param data the data to set */ public void setData(String data) { this.data = data; } /** * @return the key */ public float getKey() { return key; } /** * @param key the key to set */ public void setKey(float key) { this.key = key; } } public class Huffman { /** * * @Title: getHuffmanCodeNode * @Description: 获取huffman树结构 * @param list * @return * @return HuffmanNode * @throws */ public static HuffmanNode getHuffmanCodeNode(ArrayList<HuffmanNode> list){ 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); //将合并后的结点加入到原队列 } return list.get(0); } /** * * @Title: sortNodeListByKey * @Description:按key值单调递增排序结点队列 * @param list * @return void * @throws */ public static void sortNodeListByKey(ArrayList<HuffmanNode> list){ for(int i = 0; i < list.size(); i++){ for(int j = 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: 合并最小的结点 * @param left * @param right * @return * @return HuffmanNode * @throws */ public static HuffmanNode combine2SmallestNode(HuffmanNode left, HuffmanNode right){ HuffmanNode parentNode = new HuffmanNode(left, right, left.getData() + right.getData(), left.getKey() + right.getKey()); return parentNode; } /** * * @Title: printHuffmanNode * @Description: 打印结点(前序) * @param node * @return void * @throws */ public static void printHuffmanNode(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程序 * @param args * @return void * @throws */ public static void main(String[] args) { // TODO Auto-generated method stub ArrayList<HuffmanNode> list = new ArrayList<HuffmanNode>(); list.add(new HuffmanNode(null, null, "A", 0.3f)); list.add(new HuffmanNode(null, null, "B", 0.1f)); list.add(new HuffmanNode(null, null, "C", 0.35f)); list.add(new HuffmanNode(null, null, "D", 0.05f)); list.add(new HuffmanNode(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