赫夫曼编码
一、基础知识总结
赫夫曼编码是一种变长编码。
赋予高频词的字符较短的码字,低频次的字符较长的码字。
所谓前缀码是没有任何码字是其他码字的前缀,使用前缀码使得在解码过程中更加简单无歧义。
二叉树结构对于生成前缀码有特殊的优势,因为每一个叶节点到其他叶节点之间无关联,无法找到一个叶节点到其他叶节点的连续简单路径。
这样的性质很好的满足和前缀码的需求。
构造赫夫曼树利用了贪心的算法思想,维护一个优先队列,队列中初始状态为单节点子树,节点的值为字符权重。
每次从优先队列中挑出最小和次小元素,进行合并,合并后的局部根节点为两子节点的值之和,然后把该合并后的子树加入优先队列中。
重复以上过程直到优先队列中只剩一个元素,即为赫夫曼树的根节点。
更一般的讲,该算法思想能够构造一棵具有n个叶节点的最小加权路径长度的二叉树。其中加权路径长度定义为:
常见的应用常见比如:构造决策树。
二、算法实现
package aggreedy; import java.util.Comparator; import java.util.Deque; import java.util.PriorityQueue; public class HuffmanTree { // 树节点的数据结构 static class Node { private int weight = 0; private Node left = null; private Node right = null; Node(int w){ this.weight = w; } } // 构造赫夫曼树 public static Node createHuffmanTree(double[] w){ Comparator<Node> cmp = new Comparator<Node>() {// 实例化比较器 @Override public int compare(Node node1, Node node2) { if (node2.weight > node1.weight) { return -1; }else if(node2.weight == node1.weight){ return 0; }else { return 1; } } }; PriorityQueue<Node> queen = new PriorityQueue<Node>(10,cmp); for (int i = 0; i < w.length; i++) {// 初始状态为单节点子树 queen.add(new Node((int)(w[i]*100))); } Node tmpNode1,tmpNode2,tmpMerge; while(queen.size()>1){ // 取出优先队列中最小和次小的元素,合并成新的子树,并加入队列 tmpNode1 = queen.poll(); tmpNode2 = queen.poll(); tmpMerge = new Node(tmpNode1.weight + tmpNode2.weight); tmpMerge.left = tmpNode1; tmpMerge.right = tmpNode2; queen.add(tmpMerge); } return queen.poll(); } // 先序遍历赫夫曼树 public static void preTravel(Node node){ if (node != null) { System.out.println(node.weight); preTravel(node.left); preTravel(node.right); } } // 工具方法,判定是否为叶节点 private static boolean isLeaf(Node node){ return (node.left == null) && (node.right == null); } // 递归实现编码:左字数编码0,右子树编码为1 private static void innerCode(Node node,String code){ if (!isLeaf(node)) { StringBuilder tmpCode1 = new StringBuilder(code); if (node.left != null) { tmpCode1.append("0"); } innerCode(node.left, tmpCode1.toString()); StringBuilder tmpCode2 = new StringBuilder(code); if (node.right != null) { tmpCode2.append("1"); } innerCode(node.right, tmpCode2.toString()); }else { System.out.println(code); } } public static void code(Node node){// 根据赫夫曼树进行编码 innerCode(node, ""); } public static void main(String[] args) { // TODO Auto-generated method stub double[] ary = {0.15,0.10,0.30,0.20,0.20,0.05}; // double[] ary = {0.10,0.15,0.20,0.20,0.35}; Node root = createHuffmanTree(ary); // preTravel(root); code(root); } }
三、参考资料
1、算法导论.第十六章
2、算法设计与分析基础.第九章