赫夫曼编码

一、基础知识总结

赫夫曼编码是一种变长编码。

赋予高频词的字符较短的码字,低频次的字符较长的码字。

所谓前缀码是没有任何码字是其他码字的前缀,使用前缀码使得在解码过程中更加简单无歧义。

二叉树结构对于生成前缀码有特殊的优势,因为每一个叶节点到其他叶节点之间无关联,无法找到一个叶节点到其他叶节点的连续简单路径。

这样的性质很好的满足和前缀码的需求。

构造赫夫曼树利用了贪心的算法思想,维护一个优先队列,队列中初始状态为单节点子树,节点的值为字符权重。

每次从优先队列中挑出最小和次小元素,进行合并,合并后的局部根节点为两子节点的值之和,然后把该合并后的子树加入优先队列中。

重复以上过程直到优先队列中只剩一个元素,即为赫夫曼树的根节点。

更一般的讲,该算法思想能够构造一棵具有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、算法设计与分析基础.第九章

posted @ 2017-11-13 11:18  Qcer  阅读(1136)  评论(0编辑  收藏  举报