赫夫蔓编码

问题:形如:abbcddadaa,这样的字符串,每个字符(a,b,c,d)用0和1组合(如0,01,10,111,001),怎么让字符串整体编码后最短.并且没有歧异.

假如a:0   b:1   c:01   d:10,这样是有歧异的.01可以看作c ,也可以看作ab. 

用空格来隔开每个字符,是个伪操作。空格也是一个符号。这样只能用1表示分割。而只有一个0来表示符号了。可以证明没有二叉树好。

联想到二叉树,假如每个根结点是0和1.那么每个叶子都不会有歧义.

不同型态的二叉树可供的选择有0,10,110,111.     或 00,01,10,11等等组合.

如何使‘abbcddadaa’用01,编码后最短呢?当然是 每个字符对应的编码的长度乘出现的次数就可以了。

解答:1,无歧义:考虑树的叶子,任何叶子走过的路,每步都是和其他叶子是不同的。也就是给出一个叶子的编码,是不会有任何叶子和它的子编码一样。也就是无歧义。

           2. 字符串最短,就是每个字符对应的编码的长度乘出现的次数之和。

而这就是赫夫蔓树

赫夫蔓树定义:如果每个叶子是有权重的.  那么所有叶子到根的距离乘权重之和(wpl)最小的树,就是赫夫蔓树.

构建赫夫蔓树:选择2个偏僻的字符,也就是权重最低,组成2叉树,删除 2个叶子,加入到剩下的字符中,再选择2个最偏僻的2个叶子,一直循环,以至所有字符都表示为了叶子.就是赫夫蔓树.

让他们的根做为一个新的叶子,权重为2叶之和,

为什么,公式证明是归纳+反证法。有点晦涩。

说人话:

1.假设只有2个叶子,那么2个节点立马组合成一个单节点二叉树是最小树。

2.如果有颗树是最小树, 假如某个叶子Z分裂出了2个叶子,并且这2个叶子x,y的值 x+y=z ,且 x,y是新树所有叶子中比值最小。

3.我们要做的就是证明第二步操作生成的树还是最小树。

4。如果证明出来了。那么可以看出,上面的分裂操作和赫夫曼的堆积操作本质是一致的,只是方向刚好相反。也就证明了赫夫曼是正确的。

5.要证明首先有一个定理:给定的节点,组成的最小树中,肯定有个树,他的最深的叶子是2个最小值组成的。这个定理用反证法很好处理,可以优先证明这个定理。

6.现在用反证法证明第二步操作生成的树还是最小树。

T1:第二步中未分裂树

T2:第二步中分裂出x,y的树

T2':假设是分裂出x,y的最优树

T1':假设是T2'的未分裂之前的树。

反证的思路回顾下,根据已知条件,假设一个结果,推导出和已知条件矛盾,那么假设失败。

所以我们假设T2不是最优树,T2'是。最理想我们应该推出T1不是最优树,那么T2不是最优树这个假设就不成立。完结。

开始,假设T2不是最优树,T2'是,根据定理,T2'<T2         等同于           T1'+X+Y<T1+X+Y  等同于  T1'<T1

也就是说T1不是一个最小树和前提有矛盾。假设不成立。完结

 

赫夫蔓树的扩展:

 

找了个证明.没看.先放着.不过大概想下,有贪心算法的思想。权重之和(wpl)最小,那么就选2个最小的,组合,并相加,放入剩下的。再比较。又还有动态算法的思想。

定理1:哈夫曼树是最优的。

证明:用归纳法证明。

  • 基本情况: 当 n=2 时, 哈夫曼树具有最小权重外部路径(EPW),因为树 仅有二种可能,有二个叶结点的二种哈夫曼树下的EPW是相同的。
  • 假设: 设有哈夫曼树有 n个叶子时,定理成立。
  • 推导: 令T为有n (n>=2)个叶子的哈夫曼树。不失 一般性,设 w1 <= w2 <=... <=wn。令是w1 与w2的父结点。由引理1知, 在T中,不存在叶结点,其深度大于叶结点w1 与w2的深度。若存在深度大于w1, w2深度的结点,我们可以通过将之与w1, w2交换,由此得到更小的WPL。按如下方式得到到二叉树T':以结点V'替换结点V, 其中V'的权重是w1+w2,则T'是相应于{w1+w2,w3,...,wn}的一棵哈夫曼树。根据归纳假设,T'具有最小权重外部路径,T是最优的(EPW最小)。在T'的结点V'上添加叶结点w1, w2,可得T,则T是具有最小权重外部路径的哈夫曼树。由此,我们由数学照片纳法证明了定理1. 

 

 

//自设场景。
    //假设一个游戏的按键,w,a,s,d,j,k,l 的预估使用频率为8,7,5,8,40,17,15.
    //设计一个二进制编码,使得编码最短,并计算比等长,压缩率大概高多少?
    
    public static void bitCode()
    {
        operateChar wChar=new operateChar('w', 8);
        operateChar aChar=new operateChar('a', 7);
        operateChar sChar=new operateChar('s', 5);
        operateChar dChar=new operateChar('d', 8);
        operateChar jChar=new operateChar('j', 40);
        operateChar kChar=new operateChar('k', 17);
        operateChar lChar=new operateChar('l', 15);
        
        List<MyBinaryNode<operateChar>> weightList=new ArrayList<MyBinaryNode<operateChar>>();
        weightList.add(new MyBinaryNode<Test.operateChar>(wChar, null, null));
        weightList.add(new MyBinaryNode<Test.operateChar>(aChar, null, null));
        weightList.add(new MyBinaryNode<Test.operateChar>(sChar, null, null));
        weightList.add(new MyBinaryNode<Test.operateChar>(dChar, null, null));
        weightList.add(new MyBinaryNode<Test.operateChar>(jChar, null, null));
        weightList.add(new MyBinaryNode<Test.operateChar>(kChar, null, null));
        weightList.add(new MyBinaryNode<Test.operateChar>(lChar, null, null));
        
        mycompare theCompare=new mycompare();
        weightList.sort(theCompare);
        
        //create hafuman tree.
        MyBinaryTree<operateChar> theTree=new MyBinaryTree<operateChar>(null);
        MyBinaryNode<operateChar> theRoot=theTree.rootNode;
        
        while(weightList.size()>=2)
        {
            MyBinaryNode<operateChar> min1=weightList.get(0);
            MyBinaryNode<operateChar> min2=weightList.get(1);
            operateChar tempChar=new operateChar('-', min1.element.weight+min2.element.weight);
            
            MyBinaryNode<operateChar> tempRoot=new MyBinaryNode<Test.operateChar>(tempChar, min1, min2);
            
            weightList.remove(0);
            weightList.remove(0);
            weightList.add(0, tempRoot);
            weightList.sort(theCompare);
            theTree.rootNode=tempRoot;
        }
        //set code.
        setCode(theTree.rootNode, "");
        
        for(int i=0;i<tempret.size();i++)
        {
            //System.out.println(tempret.get(i).element.vv+":"+tempret.get(i).element.weight+". code:"+tempret.get(i).element.binaryCode+"\r\n");
        }
        
        
        int normalSize=0;
        int hefumanSize=0;
        
        for(int i=0;i<tempret.size();i++)
        {
            hefumanSize+=tempret.get(i).element.binaryCode.length()*tempret.get(i).element.weight;
            normalSize+=3*tempret.get(i).element.weight;
        }
        double rate=(double)hefumanSize/(double)normalSize;
        System.out.println("hefuman 压缩为原来的:"+rate);//等长的话,7个符号,最小长度是3位。
        
        //theTree.printTree(0);
    }
    
    private static List<MyBinaryNode<operateChar>> tempret=new ArrayList<MyBinaryTree.MyBinaryNode<operateChar>>();
    
    private static void setCode(MyBinaryNode<operateChar> rootnode,String code)
    {
        if(rootnode!=null)
        {
            rootnode.element.binaryCode=code;
            if(rootnode.leftNode==null && rootnode.rightNode==null)
            {
                tempret.add(rootnode);
            }
        }
        if(rootnode.leftNode!=null)
        {
            setCode(rootnode.leftNode, code+"0");
        }
        if(rootnode.rightNode!=null)
        {
            setCode(rootnode.rightNode, code+"1");
        }
        
    }

 

posted @ 2009-10-22 10:26  琴鸟  阅读(384)  评论(0编辑  收藏  举报