java实现哈夫曼编码解压

java实现哈夫曼编码解压

压缩:java实现哈夫曼编码压缩 - CoderDreams - 博客园 (cnblogs.com)

首先写一个辅助的方法(用来将byte转成二进制字符串)

/**
 * 将一个byte转成一个二进制的字符串
 *
 * @param flag 标志是否需要补高位,true表示需要,false表示不需要
 * @return 按补码返回
 */
public static String byteToBitString(boolean flag, byte b) {
    int temp = b;

    // 如果是最后一个字节不需要补高位
    if (flag) {
        // 按位与
        temp |= 256;
    }

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

    return str;
}

解码

/**
 * 解码
 *
 * @param huffmanCodes 编码表
 * @param bytes        需要解码的字节数组
 */
private static byte[] unZip(HashMap<Byte, String> huffmanCodes, byte[] bytes) {
    // 拿到对应的字符串
    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
        // 判断是不是最后一个字节
        boolean flag = (i == bytes.length - 1);
        // 不是则为真
        String s = byteToBitString(!flag, bytes[i]);
        stringBuilder.append(s);
    }

    // 拿到之后按照编码表解码
    // 先将编码表调换
    HashMap<String, Byte> unzipMap = new HashMap<>();
    Set<Map.Entry<Byte, String>> entries = huffmanCodes.entrySet();
    for (Map.Entry<Byte, String> entry : entries) {
        unzipMap.put(entry.getValue(), entry.getKey());
    }

    // 创建集合存放byte
    ArrayList<Byte> byteArrayList = new ArrayList<>();

    // 解码并存放
    for (int i = 0; i < stringBuilder.length();) {
        // 小计数器
        int count = 1;
        boolean flag = true;
        Byte b = null;

        while (flag) {
            // 从i开始取出n个字节进行判断
            String value = stringBuilder.substring(i, i + count);
            b = unzipMap.get(value);
            if (b == null) {
                ++count;
            } else {
                flag = false;
            }
        }
        byteArrayList.add(b);
        i += count;
    }

    // 把list转成数组返回
    byte[] result = new byte[byteArrayList.size()];
    for (int i = 0; i < byteArrayList.size(); i++) {
        result[i] = byteArrayList.get(i);
    }
    return result;
}

测试代码:结合压缩

public class HuffmanEncodingDemo {

    public static void main(String[] args) {
        // 需要被转换的字符串
        String text = "i like like like java do you like a java";
        // 压缩
        byte[] bytes = huffmanZip(text);
        // 解压
        byte[] unZip = unZip(huffmanCodes, bytes);
        String s = new String(unZip);
        System.out.println(s);
    }

    /**
     * 解码
     *
     * @param huffmanCodes 编码表
     * @param bytes        需要解码的字节数组
     */
    private static byte[] unZip(HashMap<Byte, String> huffmanCodes, byte[] bytes) {
        // 拿到对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            // 判断是不是最后一个字节
            boolean flag = (i == bytes.length - 1);
            // 不是则为真
            String s = byteToBitString(!flag, bytes[i]);
            stringBuilder.append(s);
        }

        // 拿到之后按照编码表解码
        // 先将编码表调换
        HashMap<String, Byte> unzipMap = new HashMap<>();
        Set<Map.Entry<Byte, String>> entries = huffmanCodes.entrySet();
        for (Map.Entry<Byte, String> entry : entries) {
            unzipMap.put(entry.getValue(), entry.getKey());
        }

        // 创建集合存放byte
        ArrayList<Byte> byteArrayList = new ArrayList<>();

        // 解码并存放
        for (int i = 0; i < stringBuilder.length();) {
            // 小计数器
            int count = 1;
            boolean flag = true;
            Byte b = null;

            while (flag) {
                // 从i开始取出n个字节进行判断
                String value = stringBuilder.substring(i, i + count);
                b = unzipMap.get(value);
                if (b == null) {
                    ++count;
                } else {
                    flag = false;
                }
            }
            byteArrayList.add(b);
            i += count;
        }

        // 把list转成数组返回
        byte[] result = new byte[byteArrayList.size()];
        for (int i = 0; i < byteArrayList.size(); i++) {
            result[i] = byteArrayList.get(i);
        }
        return result;
    }

    /**
     * 将一个byte转成一个二进制的字符串
     *
     * @param flag 标志是否需要补高位,true表示需要,false表示不需要
     * @return 按补码返回
     */
    public static String byteToBitString(boolean flag, byte b) {
        int temp = b;

        // 如果是最后一个字节不需要补高位
        if (flag) {
            // 按位与
            temp |= 256;
        }

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

        return str;
    }

    /**
     * 封装,方便调用
     *
     * @param str 传入的字符串
     * @return 经过哈夫曼编码后的字节数组
     */
    private static byte[] huffmanZip(String str) {
        byte[] bytes = str.getBytes();
        // 转成Node集合
        List<Node> nodes = getNodes(bytes);
        // 创建哈夫曼树
        Node huffmanTree = createHuffmanTree(nodes);
        // 创建编码表
        HashMap<Byte, String> encoding = createEncoding(huffmanTree);
        // 压缩并返回
        return zip(bytes, huffmanCodes);
    }

    /**
     * 将字节数组根据编码表压缩
     *
     * @param bytes 被转换的字节数组
     * @return 压缩后的数组
     */
    private static byte[] zip(byte[] bytes, HashMap<Byte, String> huffmanCodes) {
        StringBuilder builder = new StringBuilder();
        // 遍历bytes
        for (byte b : bytes) {
            // 根据编码表拿到对应的编码
            String code = huffmanCodes.get(b);
            builder.append(code);
        }
        // 编码后再次压缩,转成byte数组
        // 先得到byte[]的长度
        int length = builder.length();
        int len;
        // 或者写成 len = (length + 7) / 8;
        if (length % 8 == 0) {
            len = length / 8;
        } else {
            len = length / 8 + 1;
        }
        byte[] result = new byte[len];
        // 每8位对应
        int index = 0;
        for (int i = 0; i < length; i += 8) {
            String str;
            if (i + 8 < length) {
                // 够八位
                str = builder.substring(i, i + 8);
            } else {
                // 最后不够八位
                str = builder.substring(i);
            }
            // 转成一个byte存入
            result[index++] = (byte) Integer.parseInt(str, 2);
        }
        return result;
    }


    static HashMap<Byte, String> huffmanCodes = new HashMap<>();
    static StringBuilder stringbuilder = new StringBuilder();

    /**
     * 为了方便调用和使用,重载
     */
    private static HashMap<Byte, String> createEncoding(Node root) {
        if (root == null) {
            return null;
        }
        // 为了减少root的一次
        createEncoding(root.left, "0", stringbuilder);
        createEncoding(root.right, "1", stringbuilder);
        return huffmanCodes;
    }

    /**
     * 根据哈夫曼树创建对应的哈夫曼编码表
     *
     * @param node          传入的节点
     * @param code          路径:左字节为0,右子节点为1
     * @param stringBuilder 用于拼接路径
     */
    private static void createEncoding(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder builder = new StringBuilder(stringBuilder);
        builder.append(code);

        if (node != null) {
            // 判断是不是应该编码的
            if (node.data == null) {
                // 左右递归
                createEncoding(node.left, "0", builder);
                createEncoding(node.right, "1", builder);
            } else {
                // 如果是
                huffmanCodes.put(node.data, builder.toString());
            }
        }
    }

    /**
     * 根据Node集合创建哈夫曼树
     */
    private static Node createHuffmanTree(List<Node> nodes) {
        while (nodes.size() > 1) {
            Collections.sort(nodes);

            Node left = nodes.get(0);
            Node right = nodes.get(1);

            // 通过另一个构造器
            Node parent = new Node(left.weight + right.weight);
            parent.left = left;
            parent.right = right;

            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
        }
        return nodes.get(0);
    }

    /**
     * 将字符数组按出现个数转换成一个个Node,并存到List返回
     *
     * @param bytes 被转换的字符数组
     * @return 转换后的list
     */
    private static List<Node> getNodes(byte[] bytes) {
        ArrayList<Node> nodes = new ArrayList<>();
        // 遍历统计出现的次数
        HashMap<Byte, Integer> map = new HashMap<>();
        for (byte b : bytes) {
            Integer integer = map.get(b);
            if (integer == null) {
                map.put(b, 1);
            } else {
                map.put(b, integer + 1);
            }
        }
        // 将map里的元素转成一个个Node
        Set<Map.Entry<Byte, Integer>> entries = map.entrySet();
        for (Map.Entry<Byte, Integer> entry : entries) {
            Node node = new Node(entry.getKey(), entry.getValue());
            // 并加入list
            nodes.add(node);
        }
        return nodes;
    }
}

/**
 * 节点类
 */
class Node implements Comparable<Node> {
    Byte data;
    int weight;

    Node left;
    Node right;

    public Node(int weight) {
        this.weight = weight;
    }

    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    /**
     * 前序遍历
     */
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }
}
posted @ 2022-04-20 17:26  CoderCatIce  阅读(169)  评论(0编辑  收藏  举报