Hufuman编码

原理

http://bitjoy.net/2016/08/18/the-implementation-of-huffman-code/
哈弗曼编码是一个很经典的压缩算法,压缩率能达到50%,甚至更低。它的基本原理包括四个步骤:
1. 统计文件中每个字符出现的频率。
2. 构建一个哈弗曼树。建树的过程是不断的合并频率最小的两个节点,父亲节点的频率为两个孩子节点的频率之和。如此循环直到合并成一个根节点。叶子节点为不同的字符及其频率。
3. 生成哈弗曼编码。从树根开始对树进行编码,比如进入左孩子的边标记为0,进入右孩子的边标记为1,这里的0和1都是二进制位。这样之后,每个叶子节点都有一个唯一的二进制编码,这就是哈弗曼编码。频率越低的字符哈弗曼编码越长,频率越高的字符哈弗曼编码越短,这样就能起到压缩的效果。
4. 第二遍扫描文件,把字符转换为对应的哈弗曼编码,保存成压缩文件。
解压缩的过程就是解析二进制位,然后查找哈弗曼树,每找到一个叶子节点,就解析出一个字符,直到解析完所有二进制位。

那么如何构造哈夫曼树呢

如何构造哈夫曼树呢?
由于赫夫曼树中没有度为1的节点,则一棵具有n个叶子节点的的赫夫曼树共有2n-1个节点( 具体原因请看链接 http://blog.csdn.net/yuzhiyun3536/article/details/77720771 ),因此可以将这些节点存储在大小为2n-1的一维数组(我的下面代码中用的是ArrayList,当然其实是一样的)中。
首先把那些叶子节点存进数组,下标范围为( 0到n-1 )
之后一个for循环,i从n开始不断找出 下标为0到 i-1 之间 的两个weight最小的节点(s1 和 s2),然后构造一个父亲节点,并且更新这个父亲节点以及s1 和 s2对象的成员变量(left, right, parent),最后把这个节点添加加进数组。
慢慢地,这个数组就填充满了。哈夫曼树也就构造出来了。

代码

//数组里面的权值对应了空格以及abcd......xyz,然后进行Huffman编码

package tree;

import java.util.ArrayList;

public class testHuffmanTree {
    public static void main(String[] args) {
        //the weight of leafNode
        int weight[]={186,64,13,22,32,103,21,15,47,57,
                        1, 5,32,20,57, 63,15, 1,48,51,
                       80,23,8,18,1,16,1};  
        //构造哈夫曼树                   
        HuffmanTree tree =new HuffmanTree(weight);
        //生成哈夫曼编码
        tree.HuffmanCoding();
        //打印
        tree.printHuffmanCoding();

    }
}
class HuffmanTreeNode{
    int weight;
    //可以看出我们仅用下标来表示,而不是对象引用的形式。
    int parent,lchild,rchild;
    huffmanTreeNode(){}
    huffmanTreeNode(int weight,int parent,int lchild,int rchild){
        this.weight=weight; 
        this.parent=parent;
        this.lchild=lchild;
        this.rchild=rchild;
    }
}
class HuffmanTree{
    private int n;//the number of leafNodes
    private int m;
    private int[] weight;
    /*the number of all Nodes *不能在这里写m=2*n-1;否则m只是会等于1. */
    //存储哈夫曼树的节点,其中0~n-1存储叶子节点(也就是最关键的)
    private ArrayList<huffmanTreeNode>  huffmanTree=new ArrayList<huffmanTreeNode>();
    //存储每个叶子节点的编码
    private char[][] Coding;
    huffmanTree(){}
    huffmanTree(int[] weight){
        this.weight=weight; 
        //如果m,n 不在这里初始化,在其他函数中初始化是会随即销毁的
        n=weight.length;
        m=2*n-1;
        creatHuffman();
    } 
    public ArrayList<huffmanTreeNode> getHuffmanTree() {
        return huffmanTree;
    }
    /* *选出权值最小的节点下标,范围为0~ endOfArrayList *只考虑节点的parent为0的节点,因为不等于0就表示这个节点已经有父亲了,不能再参与选择 * */
    int Select(int endOfArrayList){
        int minimun=1000000000;
        int current=-1;
        for(int i=0;i<=endOfArrayList;i++){
            if(0!=huffmanTree.get(i).parent)
                continue;
            if(huffmanTree.get(i).weight<minimun){
                minimun=huffmanTree.get(i).weight;  
                current=i;              
            }           
        }
        return current; 
    }
    //creatHuffman
    void creatHuffman(){        
        for(int i=0;i<n;i++){
            huffmanTree.add(new huffmanTreeNode(weight[i],0,0,0));          
        }   
        //parent初始化为0,在选择最小权值节点的时候,这个可以用来做判断条件,不为0 就表示已经有父亲了,不再参与选择
        for(int i=n;i<m;i++){
            huffmanTree.add(new HuffmanTreeNode(0,0,0,0));
        }
        for(int i=n;i<m;i++ ){
            int s1,s2;
            s1=Select(i-1);         
            huffmanTree.get(s1).parent=i;
            s2=Select(i-1);         
            huffmanTree.get(s2).parent=i;
            //创建新的节点
            HuffmanTreeNode parentNode=new HuffmanTreeNode(0,0,0,0); 
            parentNode.lchild=s1;
            parentNode.rchild=s2;           
            parentNode.weight=
                    huffmanTree.get(s1).weight+
                    huffmanTree.get(s2).weight; 
           //把这个新的节点加进list
           huffmanTree.add(   parentNode  );
        }       
    }
    //getHuffmanCoding
    void  HuffmanCoding(){
        Coding=new char[n][n];  
        for(int i=0;i<n;i++){
            int start=n-1;
            for(int c=i, f=huffmanTree.get(i).parent;  f!=0;  c=f,f=huffmanTree.get(f).parent){
                if(huffmanTree.get(f).lchild==c)
                    Coding[i][start--]='0';//左分支为‘0’
                else 
                    Coding[i][start--]='1'; 
            }                   
        }   
    }
    //printHuffmanCoding
    void printHuffmanCoding(){
        char letter=97;//对应的字母
        System.out.println("the codes are as following ******" );
        for(int i=0;i<n;i++){//第一个是空格,额外考虑
            if(i!=0){
                System.out.print(letter+" : "  );
                letter++;           
            }
            else 
                System.out.print("空格"+" : "  );        
            for(int j=0;j<n;j++){
                if(Coding[i][j]!='#')
                    System.out.print(Coding[i][j]+" ");
            }
            System.out.println();
        }   
    }
}

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注