zoukankan      html  css  js  c++  java
  • 20172328--蓝墨云班课实验--哈夫曼树的编码

    哈夫曼编码测试

    任务详情

    • 设有字符集: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)把实验结果截图上传到云班课

    任务分析与分析思路

    1. 首先要把字符集从文件中读出来,并保存在一个数组里面。这一步暂时想用IO流来实现。
    2. 紧接着是去计算每一个字符出现的频率。初步想用嵌套循环来实现,并把出现的概率存在另一个数组中。
    3. 然后通过这两个步骤,构建出一个二维数组,每一个字符对应一个出现的频率。
    4. 然后写一个compareTo方法,对该二维数组的第二个元素进行排序,得到从小到大的一个顺序。
    5. 用构造哈夫曼树的方法,依次递归,找到最小的两个元素然后相加构成一个子树,直到最终的和为1结束。
    6. 通过遍历哈夫曼树,得到每一个元素的编码值,并存进一个新的数组code[]中。
    7. 对从文件中读出的数据进行遍历,并与code[]中的字符进行比较,如若相等,则转换为对应的变码。直至遍历结束,哈夫曼编码完成。
    8. 对于解码,暂时不知道怎么完成。。。。

    真正的解决过程

    • 《FIRST》:将文件读出来就让我卡壳了,万事真的开头也难。。。

    • 我百度了一下如何读写文件:

    • 通过学习,我照着编写的读文件的代码:

    File file = new File("D:\20172328lxy\20172328lxy\src\week_10\homework\text.txt");
            Reader reader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(reader);
            String temp = bufferedReader.readLine();
    
    • 同样地,写文件的代码:
     File file2 = new File("D:\20172328lxy\20172328lxy\src\week_10\homework\ciphertext.txt");
            Writer writer = new FileWriter(file2);
            writer.write(result1);
            writer.close();
    

    这样就实现了我预期中的第一步。从把字符集从文件中读出来,并保存在一个数组characters[]里面。

    • 《SECOND》:然后进行计算每一个字符出现的频率。首先用嵌套循环来实现,并把出现的概率存在另一个数组中。真正地初次编完代码,好的,全是0。我继续改,知道在嵌套循环中逻辑没有错误,但是全是0.0。

    • 这就让我有点百思不得其解了,为什么逻辑没有错误,但概率不出现,细细分析一遍,其实是我的除法有问题,在"/"运算中,计算机会自动忽略后面的小数位,所以需要加(float)的操作才化腐朽为神奇。。。(如下图所示)

    • 算频率的时候我用了ASCII表的位置来比对,我觉得自己的这个方法很巧妙。(可能只是我觉得
      还有多种方法可以实现频率的计算~~

    double frequency[] = new double[27];
            int numbers = 0;//空格的个数
            for (int i = 0; i < characters.length; i++) {
                if (characters[i] == ' ') {
                    numbers++;
                }
                frequency[26] = (float) numbers / characters.length;
            }
            System.out.println("字符集为");
            for (int j = 97; j <= 122; j++) {
                int number = 0;//给字母计数
                for (int m = 0; m < characters.length; m++) {
                    if (characters[m] == (char) j) {
                        number++;
                    }
                    frequency[j - 97] = (float) number / characters.length;
                }
                System.out.print((char) j + ",");
            }
    
    • 《THIRD》:第三部原本想通过这两个步骤,构建出一个二维数组,每一个字符对应一个出现的频率。但是细细想来,二维数组会浪费大量的空间,索性就回过头去写哈夫曼树的构成,参考了一些博客,写出了大体的代码,在广度优先遍历解决完成后,需要找到所有的叶子节点,才能知道每一个字符对应的编码,所以需要记录一个值,再添加子树的时候将“0”和“1”记录下来,最后最好输出在我的广度优先遍历结果中,添加在结点的属性里。

    • 于是我先尝试了一下,用了一个int型的变量加在了huffman树的节点类的属性中,但是出现的结果是:

    • 我找了找规律,最后发现只有0和1的话,加起来很容易就会出现这种情况,而不会反映我的编码,于是我才明白只能用String型来保存这个变量,字符串的相加才能正常出现编码,这样一来就解决了我的问题。

    public class Node implements Comparable<Node> {
        private char data;
        private double weight;
        private Node left;
        private Node right;
        String codenumber;//这个就是保存0&1的变量
    
        public Node(char data, double weight){
            this.data = data;
            this.weight = weight;
            this.codenumber ="";
        }
    

    • 《Fourth》:得到每个字符对应的编码后,就可以对文件进行编码了。
    //对英文文件进行编码,输出一个编码后的文件
            String result1 = "";
            List<Node> temp1 = breadthFirstTraversal(root);
    
            for (int i = 0; i < characters.length; i++) {
                for (int j = 0; j < temp1.size(); j++) {
    
                    if (characters[i] == temp1.get(j).getData()) {
                        result1 += temp1.get(j).getCodenumber();
                    }
                }
            }
    
    • 再写入文件:
    File file2 = new File("D:\20172328lxy\20172328lxy\src\week_10\homework\ciphertext.txt");
            Writer writer = new FileWriter(file2);
            writer.write(result1);
            writer.close();
    
    • 《Fifth》:对英文文件进行解码,输出一个解码后的文件。在这一部分我没有思路,想了很久还是没什么结果。在这一部分参考了我们班同学20172313余坤澎同学的博客,他的思路很好而且很容易让人理解!根据他的思路,我把遍历出的树保存在一个Node型的列表temp1中,再从temp1中挑出字符和编码分别存放在String型的列表newlist和String型的列表newlist1中,在使用他巧妙的暂时记录点的方法,最后实现了解码。
    //对英文文件进行解码,输出一个解码后的文件
    
            //将所有具有字符的叶子节点重新保存在一个newlist里面
            List<String> newlist = new ArrayList<>();
            for(int m=0;m < temp1.size();m++)
            {
                if(temp1.get(m).getData()!='无')
                    newlist.add(String.valueOf(temp1.get(m).getData()));
            }
            System.out.println("字符:"+newlist);
    
            List<String> newlist1 = new ArrayList<>();
            for(int m=0;m < temp1.size();m++)
            {
                if(temp1.get(m).getData()!='无')
                    newlist1.add(String.valueOf(temp1.get(m).getCodenumber()));
            }
            System.out.println("对应编码:"+newlist1);
    
    
    
            //先从编完码的文件中读出密文
            FileReader fileReader = new FileReader("D:\20172328lxy\20172328lxy\src\week_10\homework\ciphertext.txt");
            BufferedReader bufferedReader1 = new BufferedReader(fileReader);
            String secretline = bufferedReader1.readLine();
    
            //将读出的密文存在secretText列表中
            List<String> secretText = new ArrayList<String>();
            for (int i = 0; i < secretline.length(); i++) {
                secretText.add(secretline.charAt(i) + "");
            }
    
            //解密
            String result2 = "";//最后的解码结果
            String current="";// 临时的保存值
            while(secretText.size()>0) {
                current = current + "" + secretText.get(0);
                secretText.remove(0);
                for (int p = 0; p < newlist1.size(); p++) {
                    if (current.equals(newlist1.get(p))) {
                        result2 = result2 + "" + newlist.get(p);
                        current="";
                    }
    
                }
            }
    
            System.out.println("解码后的结果:"+result2);
            File file3 = new File("D:\20172328lxy\20172328lxy\src\week_10\homework\text1.txt");
            Writer writer1 = new FileWriter(file3);
            writer1.write(result2);
            writer.close();
        }
    

    最后的结果图


    点击找到完整代码

  • 相关阅读:
    关于Ajax中this失效
    添加时间周期一年半年季度
    回车事件
    alt与title
    关于checked="checked"却不显示选中的“对勾”
    正则表达式的使用
    关于JQ 查找不到对象的clientHeight,
    Mysql笔记之 -- 开启Mysql慢查询
    Mysql笔记之 -- 小试MYSQL主从配置
    Linux系统学习笔记之 1 一个简单的shell程序
  • 原文地址:https://www.cnblogs.com/LXY462283007/p/10100337.html
Copyright © 2011-2022 走看看