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(2^8=256)的頻率值以2-4BYTES的長度順序存儲起來,(用4Bytes的長度存儲頻率值,頻率值的表示範圍爲0至2^32-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中查詢,查到了該方法,如圖:

該方法是一個排序方法,升序排序。java

  • 問題二:Queue.offer方法不瞭解。
  • 問題二解決:查詢JAVA API,如圖:
    用於插入元素。
相關文章
相關標籤/搜索