172322 2018-2019-1 《程序设计与数据结构》哈夫曼编码测试报告

172322 2018-2019-1 《程序设计与数据结构》哈夫曼编码测试报告

  • 课程:《程序设计与数据结构》
  • 班级: 1723
  • 姓名: 张昊然
  • 学号:20172322
  • 教师:王志强
  • 助教:张之睿/张师瑜
  • 编码测试日期:2018年11月19日
  • 必修/选修: 必修

哈夫曼树

  • 在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
  • 例如,在英文中,e的出现机率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节,即8个比特。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。
  • 霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1XL1+W2XL2+W3XL3+...+WnXLn),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明霍夫曼树的WPL是最小的。

哈夫曼树的应用

  • 对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(28=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0至232-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。

哈弗曼树的实现

//  哈夫曼节点类
public class HuffmanNode<T> implements Comparable<HuffmanNode<T>> {
    private T data;  //  数据
    private double weight;  //  权重
    private HuffmanNode<T> left;
    private HuffmanNode<T> right;
    String code;  //  编码

    public HuffmanNode(T data, double weight){
        this.data = data;
        this.weight = weight;
        this.code = "";

    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public HuffmanNode<T> getLeft() {
        return left;
    }

    public void setLeft(HuffmanNode<T> left) {
        this.left = left;
    }

    public HuffmanNode<T> getRight() {
        return right;
    }

    public void setRight(HuffmanNode<T> right) {
        this.right = right;
    }

    public String getCode(){
        return code;
    }

    public void setCode(String str){
        code = str;
    }

    @Override
    public String toString(){
        return null;
    }

    @Override
    public int compareTo(HuffmanNode<T> other) {
        if(other.getWeight() > this.getWeight()){
            return 1;
        }
        if(other.getWeight() < this.getWeight()){
            return -1;
        }

        return 0;
    }
}
  • 编写ReadTxt类读取txt文件中的字母,用于将字母转化成哈夫曼编码。
//   读取文件类
public class ReadTxt {
    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',' '};
//    int[] nu = new int[26];
    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,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){
        //   26个字母加一个空格
        for(int i = 0;i<27;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;
    }
}
  • 编写HuffmanCoding类将读取的字母转化成为哈夫曼编码,并且规定左子树为0,右子树为1
public class HuffmanCoding<T> {
    public HuffmanNode<T> createTree(List<HuffmanNode<T>> nodes) {
        while (nodes.size() > 1) {
            Collections.sort(nodes);

            HuffmanNode<T> left = nodes.get(nodes.size() - 2);
            left.setCode(0 + "");  //  左子树为否(0)
            HuffmanNode<T> right = nodes.get(nodes.size() - 1);
            right.setCode(1 + "");  //  右子树为是(1)
            HuffmanNode<T> parent = new HuffmanNode<>(null, left.getWeight() + right.getWeight()); //   双亲结点的权值为左右孩子相加
            parent.setLeft(left);
            parent.setRight(right);
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
        }
        return nodes.get(0);  //  返回根结点
    }

    //   广度优先遍历以赋值
    public List<HuffmanNode<T>> BFS(HuffmanNode<T> root) {
        List<HuffmanNode<T>> list = new ArrayList<>();
        Queue<HuffmanNode<T>> queue = new ArrayDeque<>();

        if (root != null) {
            //  将元素入队列
            queue.offer(root);
            root.getLeft().setCode(root.getCode() + "0");
            root.getRight().setCode(root.getCode() + "1");
        }

        //  转化为0和1
        while (!queue.isEmpty()) {
            list.add(queue.peek());
            HuffmanNode<T> node = queue.poll();  //  获取头结点
            if (node.getLeft() != null) {
                node.getLeft().setCode(node.getCode() + "0");  //  左为0
            }
            if (node.getRight() != null) {
                node.getRight().setCode(node.getCode() + "1");  //  右为1
            }

            if (node.getLeft() != null) {
                queue.offer(node.getLeft());
            }

            if (node.getRight() != null) {
                queue.offer(node.getRight());
            }
        }
        return list;
    }
}
  • 最后编写实现类Huffmaninput.txt中读取信息,转化为哈夫曼编码后将哈夫曼编码放入output.txt中后完成测试。
public class Huffman {

    public static void main(String[] args) throws IOException {
        List<HuffmanNode<String>> list = new ArrayList<>();
        List<HuffmanNode<String>> list2;
        List<String> list3 = new ArrayList<>();
        List<String> list4 = new ArrayList<>();
        List<String> list5 = new ArrayList<>();
        String temp2 = "",temp3 = "";
        String result="";
        double num2 = 0;


        File file = new File("C:\\Users\\机械革命.000\\Desktop\\程序设计\\大二上\\哈夫曼\\input.txt");
        ReadTxt read = new ReadTxt();
        String temp = read.txtString(file);
        System.out.println("读取的文件是:" + temp);
        int[] num = read.getNumber();  //  存放出现次数的数组
        char[] chars = read.getChars();  //  存放元素的数组
        for(int i = 0;i<27;i++){
            System.out.print(chars[i]+":"+num[i]+"   ");
            list.add(new HuffmanNode<>(chars[i]+"",num[i]));
        }
        Collections.sort(list);  //  按照自然顺序排序

        System.out.println();
        HuffmanCoding huffmanTree = new HuffmanCoding();
        HuffmanNode<String> root = huffmanTree.createTree(list);

        list2 = huffmanTree.BFS(root);//  利用广度优先遍历遍历整棵树后赋值
        for(int i = 0;i<list2.size();i++){
            if(list2.get(i).getData()!=null) {
                list3.add(list2.get(i).getData());
                list4.add(list2.get(i).getCode());
            }
        }


        for(int i = 0;i<list2.size();i++){
            num2 += list2.get(i).getWeight();
        }

        Collections.sort(list3);
        System.out.println("出现的概率中第一个代表空格。");
        for(int i = 0;i<list3.size();i++){
            System.out.print(list3.get(i) + "出现的概率为:" + list2.get(i).getWeight()/num2 + "  \n");
        }




        System.out.println();

        for(int i = 0;i<list4.size();i++){
            System.out.print(list3.get(i)+"的编码为:"+list4.get(i)+" \n");
        }
        System.out.println();

        for(int i = 0;i<temp.length();i++){
            for(int j = 0;j<list3.size();j++){
                if(temp.charAt(i) == list3.get(j).charAt(0)) {
                    result += list4.get(j);
                }
            }
        }





        for(int i = 0;i<result.length();i++){
            list5.add(result.charAt(i)+"");
        }


        while (list5.size()>0){
            temp2 = temp2+"" +list5.get(0);
            list5.remove(0);
            for(int i=0;i<list4.size();i++){
                if (temp2.equals(list4.get(i))) {
                    temp3 = temp3+""+list3.get(i);
                    temp2 = "";
                }
            }
        }
        System.out.println();

        System.out.println("编码前:" + temp);
        System.out.println("编码后为:\n"+ result);
        System.out.println("解码后:"+"\n"+temp3);

        File file2 =new File("C:\\Users\\机械革命.000\\Desktop\\程序设计\\大二上\\哈夫曼\\output.txt");
        Writer out =new FileWriter(file2);
        out.write(result);  //  将哈夫曼编码放入output.txt中
        out.close();
    }
}

实现时的问题

  • 问题一:在看到Collections.sort方法时不了解该方法,也不了解如何使用。
  • 问题一解决:在JAVA API中查询,查到了该方法,如图:

该方法是一个排序方法,升序排序。

  • 问题二:Queue.offer方法不了解。
  • 问题二解决:查询JAVA API,如图:

    用于插入元素。
posted @ 2018-12-11 00:33  zhangyeye233  阅读(516)  评论(0编辑  收藏  举报