20172332 2017-2018-2 《程序設計與數據結構》Java哈夫曼編碼實驗--哈夫曼樹的創建,編碼與解碼

20172332 2017-2018-2 《程序設計與數據結構》Java哈夫曼編碼實驗--哈夫曼樹的創建,編碼與解碼

哈夫曼樹

  • 一、路徑和路徑長度
  • 在一棵樹中,從一個結點往下能夠達到的孩子或孫子結點之間的通路,稱爲路徑。通路中分支的數目稱爲路徑長度。若規定根結點的層數爲1,則從根結點到第L層結點的路徑長度爲L-1。
  • 二、結點的權及帶權路徑長度
  • 若將樹中結點賦給一個有着某種含義的數值,則這個數值稱爲該結點的權。結點的帶權路徑長度爲:從根結點到該結點之間的路徑長度與該結點的權的乘積。
  • 三、樹的帶權路徑長度
  • 樹的帶權路徑長度規定爲全部葉子結點的帶權路徑長度之和,記爲WPL。

哈夫曼編碼

  • 問題概述:
    在數據傳輸中,須要將傳輸的文字換成二進制的字符,用0和1的不一樣排列來表示字符。然而,在傳送報文的時候總但願傳輸報文的總長度儘量短。在實際的應用中,各個字符的出現頻率或使用次數是不一樣的,在設計編碼時,應使用頻率高的用短碼,使用頻率低的用長碼。
  • 基本思路
    對於數據通訊中的報文編碼問題,通常採用如下三步來解決:
    統計每一個報文的出現次數;
    以報文字符爲葉結點,以字符出現的次數爲權重,構造哈夫曼樹;
    遍歷二叉樹,求報文字符編碼及報文傳輸長度。
    注意:在求文字符編碼的時候,假設到每棵左子樹的根結點的邊爲0,到右子樹的結點的邊爲1。

哈夫曼樹結點的結構

  • 能夠用一個數組存放原來的n個葉子結點和構造過程當中臨時生成的結點,數組大小爲2n-1。因此,哈夫曼樹類中有兩個成員字段:data數組用於存放結點集合;leafNum表示哈夫曼樹葉子結點的數目。而哈夫曼樹結點一共有5個域:java

關鍵代碼與解讀

  • Node類:要實現Comparable接口,比較權重,好肯定放的位置(編碼是0仍是1)。
public class Node<T> implements Comparable<Node<T>> {

    @Override
    //肯定位置
    public int compareTo(Node<T> other) {
        if(other.getWeight() > this.getWeight()){
            return 1;
        }
        if(other.getWeight() < this.getWeight()){
            return -1;
        }
        return 0;
    }
}
  • HuffmanTree類:建立樹,當還有結點時,對結點進行排序,而後左孩子爲數組中的個數-2的結點,右孩子爲數組中的個數-1的結點(用數組實現樹的那一章說過左右孩子在數組中的索引),賦予左孩子的編碼爲0,右孩子的編碼爲1,雙親結點則爲左右孩子相加的權重(也就是左右孩子的機率和),把雙親結點加入鏈表中,從鏈表中把舊的左右孩子刪除,直至鏈表中的結點只剩一個(也就是根結點)。
public Node<T> createTree(List<Node<T>> nodes) {
        while (nodes.size() > 1) {
            Collections.sort(nodes);
            Node<T> left = nodes.get(nodes.size() - 2);
            left.setCode(0 + "");
            Node<T> right = nodes.get(nodes.size() - 1);
            right.setCode(1 + "");
            Node<T> parent = new Node<T>(null, left.getWeight() + right.getWeight());
            parent.setLeft(left);
            parent.setRight(right);
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
        }
        return nodes.get(0);
    }
  • HuffmanTree類:獲得相應字符的編碼值
public List<Node<T>> breadth(Node<T> root) {
        List<Node<T>> list = new ArrayList<Node<T>>();
        Queue<Node<T>> queue = new ArrayDeque<Node<T>>();

        if (root != null) {
            queue.offer(root);
            root.getLeft().setCode(root.getCode() + "0");
            root.getRight().setCode(root.getCode() + "1");
        }

        while (!queue.isEmpty()) {
            list.add(queue.peek());
            Node<T> node = queue.poll();
            if (node.getLeft() != null)
                node.getLeft().setCode(node.getCode() + "0");
            if (node.getRight() != null)
                node.getRight().setCode(node.getCode() + "1");

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

            if (node.getRight() != null) {
                queue.offer(node.getRight());
            }
        }
        return list;
    }
  • Word類:在其中我包含了空格和回車出現的可能。
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', ' ','\n'};
    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, 0};
  • Word類:由於加了空格和回車,因此小於28,獲得每一個字符出現的次數(相似於累加的形式)
//獲得相應字符出現的次數
    public void num(String string) {
        for (int i = 0; i < 28; i++) {
            int temp = 0;
            for (int j = 0; j < string.length(); j++) {
                if (string.charAt(j) == chars[i])
                    temp++;
            }
            number[i] += temp;
        }
    }
  • HuffmanTreeTest:把讀入的存進list中並進行排序整理,輸出每一個字母出現的次數,倒數第二個顯示的爲空格,最後一個顯示的爲回車
File file = new File("abc.txt");
        Word read = new Word();
        String temp = read.txtString(file);
        System.out.println(temp);
        int[] num = read.getNumber();
        char[] chars = read.getChars();
        for (int i = 0; i < 28; i++) {
            System.out.print(chars[i] + ":" + num[i] + "   ");
            list.add(new Node<String>(chars[i] + "", num[i]));
        }
        Collections.sort(list);
        System.out.println();
  • 顯示結果:node

  • HuffmanTreeTest:計算相應字母、空格與回車出現的機率
HuffmanTree huffmanTree = new HuffmanTree();
        Node<String> root = huffmanTree.createTree(list);
        list2 = huffmanTree.breadth(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();
        }
        for (int i = 0; i < list3.size(); i++) {
            System.out.println(list3.get(i) + "出現的機率爲" + list2.get(i).getWeight() / num2 + "  ");
        }
        System.out.println();
  • 顯示結果:git

  • HuffmanTreeTest:獲得相應字母、空格與回車的對應編碼值
for (int i = 0; i < list4.size(); i++) {
            System.out.println(list3.get(i) + "的編碼爲" + list4.get(i) + " ");
        }
        System.out.println();
  • 顯示結果:數組

  • HuffmanTreeTest:對原文件進行編碼數據結構

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);
            }
        }
        System.out.println("編碼後爲:" + result);
        for (int i = 0; i < result.length(); i++) {
            list5.add(result.charAt(i) + "");
        }
  • 顯示結果(太長了只截取了部分)ide

  • HuffmanTree:進行解碼
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();
  • 顯示結果this

  • HuffmanTree:把解碼後的狀況寫入文件中
File file2 = new File("abc1.txt");
        Writer out = new FileWriter(file2);
        out.write(temp3);
        out.close();

完整代碼

讀取與寫入文本

相關文章
相關標籤/搜索