1. 程式人生 > >Java理解實現哈夫曼樹以其編碼解碼

Java理解實現哈夫曼樹以其編碼解碼

哈夫曼樹以其編碼解碼

要求:
1.從終端讀入字符集大小為n(即字元的個數),逐一輸入n個字元和相應的n個權值(即字元出現的頻度),建立哈夫曼樹,進行編碼並且輸出。
將它存於檔案hfmtree中(選做)。
2.利用已建好的哈夫曼編碼檔案hfmtree,對鍵盤輸入的正文進行譯碼。輸出字元正文,再輸出該文的二進位制碼。
[測試資料]
用下表中給出的字符集(n=27)和頻度的實際統計資料建立哈夫曼樹:
在這裡插入圖片描述
 並實現以下報文的譯碼和輸出:“THIS PROGRAM IS MY FAVORITE”。

結點程式碼:

public class HNode {

    public String code =
"";// 節點的哈夫曼編碼 public String data = "";// 節點的資料 public int count;// 節點的權值 public HNode lChild; public HNode rChild; public HNode() { } public HNode(String data, int count) { this.data = data; this.count = count; } public HNode(int count, HNode lChild,
HNode rChild) { this.count = count; this.lChild = lChild; this.rChild = rChild; } public HNode(String data, int count, HNode lChild, HNode rChild) { this.data = data; this.count = count; this.lChild = lChild; this.rChild = rChild; }
}

哈夫曼樹及編碼解碼

public class Huffman {
    private String str;// 字串    
    private HNode root;// 哈夫曼二叉樹的根節點
    private boolean flag;// 最新的字元是否已經存在的標籤
    private LinkedList<CharData> charList;// 儲存不同字元的佇列 相同字元存在同一位置
    private LinkedList<HNode> NodeList;// 儲存節點的佇列

     private class CharData {
         int num = 1; // 字元個數
         char c; // 字元

         public CharData(char ch){
             c = ch;
         }
     }

    /**
     * 構建哈夫曼樹
     */
    public void creatHfmTree(String str) {
        this.str = str;

        NodeList = new LinkedList<HNode>();
        charList = new LinkedList<CharData>();

        // 1.統計字串中字元以及字元的出現次數
        // 以CharData類來統計出現的字元和個數
        getCharNum(str);

        // 2.根據第一步的結構,建立節點
        creatNodes();

        // 3.對節點權值升序排序
        Sort(NodeList);

        // 4.取出權值最小的兩個節點,生成一個新的父節點
        // 5.刪除權值最小的兩個節點,將父節點存放到列表中
        creatTree();

        // 6.重複第四五步,就是那個while迴圈
        // 7.將最後的一個節點賦給根節點
        root = NodeList.get(0);
    }

    /**
     * 統計出現的字元及其頻率
     */
    private void getCharNum(String str) {

        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i); // 從給定的字串中取出字元
            flag = true;

            for (int j = 0; j < charList.size(); j++) {
                CharData data = charList.get(j);

                if(ch == data.c){ 
                    // 字元物件連結串列中有相同字元則將個數加1
                    data.num++;
                    flag = false;
                    break;
                }
            }

            if(flag){
                // 字元物件連結串列中沒有相同字元則建立新物件加如連結串列
                charList.add(new CharData(ch)); 
            }
        }
    }

    /**
     * 將出現的字元建立成單個的結點物件
     */
    private void creatNodes() {

        for (int i = 0; i < charList.size(); i++) {
            String data = charList.get(i).c + "";
            int count = charList.get(i).num;

            HNode node = new HNode(data, count); // 建立節點物件
            NodeList.add(node); // 加入到節點連結串列
        }

    }

    /**
     * 構建哈夫曼樹
     */
    private void creatTree() {

        while (NodeList.size() > 1) {// 當節點數目大於一時
            // 4.取出權值最小的兩個節點,生成一個新的父節點
            // 5.刪除權值最小的兩個節點,將父節點存放到列表中
            HNode left = NodeList.poll();
            HNode right = NodeList.poll();

            // 在構建哈夫曼樹時設定各個結點的哈夫曼編碼
            left.code = "0";
            right.code = "1";
            setCode(left);
            setCode(right);

            int parentWeight = left.count + right.count;// 父節點權值等於子節點權值之和
            HNode parent = new HNode(parentWeight, left, right);

            NodeList.addFirst(parent); // 將父節點置於首位
            Sort(NodeList); // 重新排序,避免新節點權值大於連結串列首個結點的權值
        }
    }

    /**
     * 升序排序
     */
    private void Sort(LinkedList<HNode> nodelist) {
        for (int i = 0; i < nodelist.size() - 1; i++) {
            for (int j = i + 1; j < nodelist.size(); j++) {
                HNode temp;
                if (nodelist.get(i).count > nodelist.get(j).count) {
                    temp = nodelist.get(i);
                    nodelist.set(i, nodelist.get(j));
                    nodelist.set(j, temp);
                }

            }
        }

    }

    /**
     * 設定結點的哈夫曼編碼
     */
    private void setCode(HNode root) {

        if (root.lChild != null) {
            root.lChild.code = root.code + "0";
            setCode(root.lChild);
        }

        if (root.rChild != null) {
            root.rChild.code = root.code + "1";
            setCode(root.rChild);
        }
    }

    /**
     * 遍歷
     */
    private void output(HNode node) {

        if (node.lChild == null && node.rChild == null) {
            System.out.println(node.data + ": " + node.code);
        }
        if (node.lChild != null) {
            output(node.lChild);
        }
        if (node.rChild != null) {
            output(node.rChild);
        }
    }

    /**
     * 輸出結果字元的哈夫曼編碼
     */
    public void output() {
        output(root);
    }

    private String hfmCodeStr = "";// 哈夫曼編碼連線成的字串

    /**
     * 編碼
     */
    public String toHufmCode(String str) {

        for (int i = 0; i < str.length(); i++) {
            String c = str.charAt(i) + "";
            search(root, c);
        }
        return hfmCodeStr;
    }

    private void search(HNode root, String c) {
        if (root.lChild == null && root.rChild == null) {
            if (c.equals(root.data)) {
                hfmCodeStr += root.code; // 找到字元,將其哈夫曼編碼拼接到最終返回二進位制字串的後面
            }
        }
        if (root.lChild != null) {
            search(root.lChild, c);
        }
        if (root.rChild != null) {
            search(root.rChild, c);
        }
    }

    // 儲存解碼的字串
    String result="";
    boolean target = false; // 解碼標記
    /**
     * 解碼
     * @param codeStr
     * @return
     */
    public String CodeToString(String codeStr) {

        int start = 0;
        int end = 1;

        while(end <= codeStr.length()){
            target = false;
            String s = codeStr.substring(start, end);
            matchCode(root, s); // 解碼
            // 每解碼一個字元,start向後移
            if(target){
                start = end;
            }
            end++;
        }
        return result;
    }
    
    private void matchCode(HNode root, String code){
        if (root.lChild == null && root.rChild == null) {
            if (code.equals(root.code)) {
                result += root.data; // 找到對應的字元,拼接到解碼字元穿後
                target = true; // 標誌置為true
            }
        }
        if (root.lChild != null) {
            matchCode(root.lChild, code);
        }
        if (root.rChild != null) {
            matchCode(root.rChild, code);
        }
    }
    
    public static void main(String[] args) {

        Huffman huff = new Huffman();// 建立哈弗曼物件
        String data = "THIS PROGRAM IS MY FAVORITE"; 
        huff.creatHfmTree(data);// 構造樹
        huff.output(); // 顯示字元的哈夫曼編碼
        // 將目標字串利用生成好的哈夫曼編碼生成對應的二進位制編碼
        String hufmCode = huff.toHufmCode(data); 
        System.out.println("編碼:" + hufmCode);
        // 將上述二進位制編碼再翻譯成字串
        System.out.println("解碼:" + huff.CodeToString(hufmCode));            
    }
}

效果如下:
在這裡插入圖片描述