赫夫曼編碼進行壓縮和解碼

package com.eleven.binaryTree.huffman.code;

import java.util.*;

/**
 * @Description 用赫夫曼編碼進行壓縮和解碼
 * @auther Eleven
 * @create 2020-04-07 23:12
 **/
public class HuffManCodeCus {

    public static void main(String[] args) {
        HuffManCodeCus huffManCodeCus = new HuffManCodeCus();
        String msg = " can you think about it . ";
        huffManCodeCus.huffManCodeCus(msg);
    }

    //創建赫夫曼編碼
    public void huffManCodeCus(String str){
        byte[] bytes = str.getBytes();
        System.out.println("壓縮前:"+bytes.length);
        System.out.println(Arrays.toString(bytes));
        //將byte數字轉化爲赫夫曼樹的node(這裏要注意所有權重的計算,是以每個數出現的次數)
        List<Node> nodes =  createNodeList(bytes);
        //將nodes轉化爲赫夫曼樹
        Node tree = createTree(nodes);
        //創建赫夫曼編碼表(所謂的編碼表就是指從根節點到葉子節點的路徑 左邊爲0右邊用1標示)
        Map<Byte,String> huffManCodeTable = createCode(tree);
       // System.out.println(huffManCodeTable);
        //根據編碼表進行壓縮
        byte[] zipBytes = zip(huffManCodeTable,bytes);
        //System.out.println("壓縮前:"+zipBytes.length);

        //解碼
        byte[] unZipBytes = unZip(huffManCodeTable,zipBytes);

        System.out.println(new String(unZipBytes));
    }

    //解碼
    private byte[] unZip(Map<Byte, String> huffManCodeTable, byte[] zipBytes) {
        Map<String, Byte> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        //首先將編碼表的鍵值對互換位置
        for (Map.Entry<Byte, String> entry:huffManCodeTable.entrySet()){
            map.put(entry.getValue(),entry.getKey());
        }
        //轉化爲8位二進制數據
        for(int i = 0;i<zipBytes.length;i++){
            byte bty = zipBytes[i];
            boolean flag=(i==zipBytes.length-1);
            String str =   byteToString(!flag,bty);
            sb.append(str);
        }
        System.out.println(sb.toString());
        //將數據轉化爲byte
        int count =0;
        List<Byte> list = new ArrayList<Byte>();
        for (int i = 0;i<sb.length();i++){
            String b  = sb.substring(count,i);
            if (map.get(b) !=null){
                list.add(map.get(b));
                count=i;
            }
        }
        //轉成數組
        byte[] bytes = new byte[list.size()];
        for (int m=0;m<list.size();m++){
            bytes[m] = list.get(m);
        }
        return bytes;
    }

    /**
     * 轉成8位二進制數據
     * @param flag 判斷是否爲最後一位
     * @param b 數組
     * @return
     */
    private String byteToString(boolean flag ,byte b) {
        int by = b;
        if (flag){
            by|=256;
        }

        String temp = Integer.toBinaryString(by);
        if (flag){
            temp = temp.substring(temp.length()-8);
        }else{
            temp = temp;
        }
        return temp;
    }


    private byte[] zip(Map<Byte, String> huffManCodeTable, byte[] bytes) {
        //轉化爲二進制編碼
        StringBuilder zipSb = new StringBuilder();
        for (int i=0;i<bytes.length;i++){
            zipSb.append(huffManCodeTable.get(bytes[i]));
        }
        System.out.println(zipSb.toString());
        //再變成壓縮後的byte數組
        //使用8爲二進制數標示所以存儲壓縮後byte的長度計算方式如下
        int len;
        if (zipSb.length()%8 ==0){
            len = zipSb.length()/8;
        }else {
            len = zipSb.length()/8+1;
        }
        byte[] zipResbytes = new byte[len];
        int index = 0;
        //每8位一截取
        for (int i=0;i<zipSb.length();i=i+8){
            String temp = "";
            if (zipSb.length()-i<8){
                temp = zipSb.substring(i);
            }else {
                temp = zipSb.substring(i,i+8);
            }
            //轉換爲二進制
           byte b = (byte) Integer.parseInt(temp,2);
            zipResbytes[index] = b;
            index++;
        }
        return zipResbytes;
    }

    static StringBuilder  sb = new StringBuilder();
    static Map<Byte, String> huffManMap = new HashMap<>();
    private Map<Byte, String> createCode(Node tree) {
        if (tree == null){
            return null;
        }
        //創建路徑編碼
        createCodes(tree.left,"0",sb);
        createCodes(tree.right,"1",sb);
        return huffManMap;
    }

    private void createCodes(Node node, String s, StringBuilder sb) {
        StringBuilder  stringBuilder = new StringBuilder(sb);
        stringBuilder.append(s);
        if (node.data==null){
            createCodes(node.left,"0",stringBuilder);
            createCodes(node.right,"1",stringBuilder);
        }else {
            huffManMap.put(node.data,stringBuilder.toString());
        }

    }

    /**
     * 創建赫夫曼樹
     * @param nodes
     * @return
     */
    private Node createTree(List<Node> nodes) {
        //首先要對集合進行排序
        while (nodes.size()>1) {
            Collections.sort(nodes);
            //取出最小的左右的節點
            Node left = nodes.get(nodes.size() - 1);
            Node right = nodes.get(nodes.size() - 2);
            //新建一個父節點 並且設置好左右節點
            Node parent = new Node(null, left.weight + right.weight);
            parent.left = left;
            parent.right = right;
            //將左右節點移除
            nodes.remove(left);
            nodes.remove(right);
            //將父節點添加進去
            nodes.add(parent);
            //再接着排序循環以上步驟
        }
        //返回根節點
        return nodes.get(0);
    }

    /**
     * 轉化爲赫夫曼樹的葉子節點
     * @param bytes
     * @return
     */
    private List<Node> createNodeList(byte[] bytes) {
        List<Node> nodes = new ArrayList<>();
        //計算數組中每個數出現的次數,作爲權重
        Map<Byte,Integer> map = new HashMap<>();
        for (int i=0;i<bytes.length;i++){
            if (map.get(bytes[i])==null){
                map.put(bytes[i],1);
            }else {
                int count = map.get(bytes[i]);
                map.put(bytes[i],count+1);
            }
        }
        //根據權重和數據創建葉子節點集合
        for (Map.Entry<Byte,Integer> entry:map.entrySet()){
            Node node = new Node(entry.getKey(),entry.getValue());
            nodes.add(node);
        }
        return nodes;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章