哈夫曼编码测试

1.哈夫曼树介绍

在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

例如,在英文中,e的出现机率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个比特。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。

霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1L1+W2L2+W3L3+...+WnLn),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明霍夫曼树的WPL是最小的。

演算过程
进行霍夫曼编码前,我们先创建一个霍夫曼树。


⒈将每个英文字母依照出现频率由小排到大,最小在左。

⒉每个字母都代表一个终端节点(叶节点),比较F.O.R.G.E.T五个字母中每个字母的出现频率,将最小的两个字母频率相加合成一个新的节点。如Fig.2所示,发现F与O的频率最小,故相加2+3=5。

⒊比较5.R.G.E.T,发现R与G的频率最小,故相加4+4=8。

⒋比较5.8.E.T,发现5与E的频率最小,故相加5+5=10。

⒌比较8.10.T,发现8与T的频率最小,故相加8+7=15。

⒍最后剩10.15,没有可以比较的对象,相加10+15=25。

进行编码

1.给霍夫曼树的所有左链结'0'与
霍夫曼树
霍夫曼树
右链结'1'。

2.从树根至树叶依序记录所有字母的编码

2.实验内容

设有字符集:S={a,b,c,d,e,f,g,h,i,j,k,l,m,n.o.p.q,r,s,t,u,v,w,x,y,z}。
给定一个包含26个英文字母的文件,统计每个字符出现的概率,根据计算的概率构造一颗哈夫曼树。
并完成对英文文件的编码和解码。
要求:
(1)准备一个包含26个英文字母的英文文件(可以不包含标点符号等),统计各个字符的概率
(2)构造哈夫曼树
(3)对英文文件进行编码,输出一个编码后的文件
(4)对编码文件进行解码,输出一个解码后的文件
(5)撰写博客记录实验的设计和实现过程,并将源代码传到码云
(6)把实验结果截图上传到云班课
满分:6分。
酌情打分。

3. 实验过程及结果

HuffmanNode类 实现哈夫曼树的结点

public class HuffmanNode   {
    private int weight;//权值
    private int parent;
    private int leftChild;
    private int rightChild;

    public HuffmanNode(int weight,int parent,int leftChild,int rightChild){
        this.weight=weight;
        this.parent=parent;
        this.leftChild=leftChild;
        this.rightChild=rightChild;
    }

    void setWeight(int weight){
        this.weight=weight;
    }

    void setParent(int parent){
        this.parent=parent;
    }

    void setLeftChild(int leftChild){
        this.leftChild=leftChild;
    }

    void setRightChild(int rightChild){
        this.rightChild=rightChild;
    }

    int getWeight(){
        return weight;
    }

    int getParent(){
        return parent;
    }

    int getLeftChild(){
        return leftChild;
    }

    int getRightChild(){
        return rightChild;
    }
}

HuffmanCode 类 记录所用的字符及对应的编码

public class HuffmanCode {
    private String character;
    private String code;
    HuffmanCode(String character,String code){
        this.character=character;
        this.code=code;
    }
    HuffmanCode(String code){
        this.code= code;
    }

    void setCharacter(String character){
        this.character=character;
    }

    void setCode(String code){
        this.code=code;
    }

    String getCharacter(){
        return character;
    }

    String getCode(){
        return code;
    }
}

HuffmanTree类,实现哈夫曼树以及每个符号获得对应的前缀码

public class HuffmanTree {
    //初始化一个huffuman树
    public static void initHuffmanTree(HuffmanNode[] huffmanTree,int m){
        for(int i=0;i<m;i++){
            huffmanTree[i] = new HuffmanNode(0,-1,-1,-1);
        }
    }

    //初始化一个huffmanCode
    public static void initHuffmanCode(HuffmanCode[] huffmanCode,int n){
        for(int i=0;i<n;i++){
            huffmanCode[i]=new HuffmanCode("","");
        }
    }

    //获取huffmanCode的符号
    public static void getHuffmanCode(HuffmanCode[] huffmanCode , int n,char[] chars){
        for(int i=0;i<n;i++){
            String temp = ""+chars[i];
            huffmanCode[i] = new HuffmanCode(temp,"");
        }
    }

    //获取huffman树节点频数
    public static void getHuffmanWeight(HuffmanNode[] huffmanTree , int n,int[] ints){
        for(int i=0;i<n;i++){
            int temp = ints[i];
            huffmanTree[i] = new HuffmanNode(temp,-1,-1,-1);
        }
    }

    //从n个结点中选取最小的两个结点
    public static int[] selectMin(HuffmanNode[] huffmanTree ,int n)
    {
        int min[] = new int[2];
        class TempNode
        {
            int newWeight;//存储权
            int place;//存储该结点所在的位置

            TempNode(int newWeight,int place){
                this.newWeight=newWeight;
                this.place=place;
            }

            void setNewWeight(int newWeight){
                this.newWeight=newWeight;
            }

            void setPlace(int place){
                this.place=place;
            }

            int getNewWeight(){
                return newWeight;
            }

            int getPlace(){
                return place;
            }
        }

        TempNode[] tempTree=new TempNode[n];

        //将huffmanTree中没有双亲的结点存储到tempTree中
        int i=0,j=0;
        for(i=0;i<n;i++)
        {
            if(huffmanTree[i].getParent()==-1&& huffmanTree[i].getWeight()!=0)
            {
                tempTree[j]= new TempNode(huffmanTree[i].getWeight(),i);
                j++;
            }
        }

        int m1,m2;
        m1=m2=0;
        for(i=0;i<j;i++)
        {
            if(tempTree[i].getNewWeight()<tempTree[m1].getNewWeight())//此处不让取到相等,是因为结点中有相同权值的时候,m1取最前的
                m1=i;
        }
        for(i=0;i<j;i++)
        {
            if(m1==m2)
                m2++;//当m1在第一个位置的时候,m2向后移一位
            if(tempTree[i].getNewWeight()<=tempTree[m2].getNewWeight()&& i!=m1)//此处取到相等,是让在结点中有相同的权值的时候,

                //m2取最后的那个。
                m2=i;
        }

        min[0]=tempTree[m1].getPlace();
        min[1]=tempTree[m2].getPlace();
        return min;
    }

    //创建huffmanTree
    public static void createHaffmanTree(HuffmanNode[] huffmanTree,int n){
        if(n<=1)
            System.out.println("Parameter Error!");
        int m = 2*n-1;
        //initHuffmanTree(huffmanTree,m);

        for(int i=n;i<m;i++)
        {
            int[] min=selectMin(huffmanTree,i);
            int min1=min[0];
            int min2=min[1];
            huffmanTree[min1].setParent(i);
            huffmanTree[min2].setParent(i);
            huffmanTree[i].setLeftChild(min1);
            huffmanTree[i].setRightChild(min2);
            huffmanTree[i].setWeight(huffmanTree[min1].getWeight()+ huffmanTree[min2].getWeight());
        }
    }

    //创建huffmanCode
    public static void createHaffmanCode(HuffmanNode[] huffmanTree,HuffmanCode[] huffmanCode,int n){
        char[] code = new char[26];
        int start;
        int c;
        int parent;
        int temp;

        code[n-1]='0';
        for(int i=0;i<n;i++)
        {
            StringBuffer stringBuffer = new StringBuffer();
            start=n-1;
            c=i;
            while( (parent=huffmanTree[c].getParent()) >=0 )
            {
                start--;
                code[start]=((huffmanTree[parent].getLeftChild()==c)?'0':'1');
                c=parent;

            }
            for(;start<n-1;start++){
                stringBuffer.append(code[start]);
            }
            huffmanCode[i].setCode(stringBuffer.toString());
        }
    }

HuffmanTest类 读取相应的文件并做出加密解密操作

import java.io.*;

import static week15.HuffmanTree.*;

public class HuffmanTest {
    private char[] chars = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s'
            ,'t','u','v','w','x','y','z'};
    private  int[] number = new int[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    public String txtString(File file){
        StringBuilder result = new StringBuilder();
        try{
            BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                result.append(System.lineSeparator()+s);
                num(s);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result.toString();
    }

    public void num(String string){
        for(int i = 0;i<26;i++){
            int temp = 0;
            for(int j = 0;j<string.length();j++){
                if(string.charAt(j) == chars[i])
                    temp++;
            }
            number[i] += temp;
        }
    }

    public int[] getNumber(){
        return number;
    }

    public char[] getChars(){
        return chars;
    }

    public static void main(String[] args){
        File file = new File("D:\\","inputhuffman.txt");
        HuffmanTest huffmanTest= new HuffmanTest();
        String temp = huffmanTest.txtString(file);
        int[] num = huffmanTest.getNumber();

        char[] chars = huffmanTest.getChars();

        int n;
        int m;
        n = 26;
        m=2*n-1;
        HuffmanNode[] huffmanTree = new HuffmanNode[m];
        HuffmanCode[] huffmanCode = new HuffmanCode[n];

        //初始化huffmanTree,huffmanCode
        initHuffmanTree(huffmanTree,m);
        initHuffmanCode(huffmanCode,n);

        //获取huffmanCode的符号
            getHuffmanCode(huffmanCode,n,chars);
        //获取huffmanTree的频数
            getHuffmanWeight(huffmanTree,n,num);

        //创建huffmanTree
        createHaffmanTree(huffmanTree,n);
        //创建huffmanCode
        createHaffmanCode(huffmanTree,huffmanCode,n);

        //输出huffmanCode编码
        ouputHaffmanCode(huffmanCode,n);

        String result = "";
        for(int i = 0;i<temp.length();i++){
            for(int j = 0;j<huffmanCode.length;j++){
                if(temp.charAt(i) == huffmanCode[j].getCharacter().charAt(0))
                    result +=huffmanCode[j].getCode();
            }
        }
        System.out.println("加密");
        System.out.println(result);
        System.out.println("解密");
}

3. 实验过程中遇到的问题和解决过程

问题1:只输出了两个数的编码

问题1解决方案:
错误地将26个字符数删成两个 于是其他地方输入多少个也没用

改正之后就没问题了

其他(感悟、思考等)

参考资料

posted @ 2018-12-12 23:56  m1sty  阅读(772)  评论(0编辑  收藏  举报