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》:將文件讀出來就讓我卡殼了,萬事真的開頭也難。。。
  • 我百度了一下如何讀寫文件:
    html

  • 經過學習,我照着編寫的讀文件的代碼:
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[]裏面。git

  • 《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();
    }

最後的結果圖


點擊找到完整代碼

相關文章
相關標籤/搜索