赫夫曼编码与解码

本文详细介绍了赫夫曼编码的过程,包括创建赫夫曼树、生成编码、压缩文件,以及如何进行赫夫曼解码和文件解压。在文件压缩中,赫夫曼编码尤其适用于重复字符较高的文本,尽管有时压缩后文件大小可能增加。由于压缩方式的特殊性,压缩后的文件需要使用特定的解压代码来还原。
摘要由CSDN通过智能技术生成

节点类

class Node implements Comparable<Node> {
   

    Byte data;
    int weight;
    Node left;
    Node right;

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

    @Override
    public String toString() {
   
        return "Node{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }

    @Override
    public int compareTo(Node o) {
   
        // 从小到大排
        return this.weight - o.weight;
    }
}

全局静态变量

	// 存放哈弗曼编码表
    static Map<Byte, String> huffmanCodesMap = new HashMap<>();

	// 用于拼接各个数据的哈夫曼编码
    static StringBuilder stringBuilder = new StringBuilder();

	// 哈夫曼编码每8位一组,以十进制存放,记录最后一组数据的位数(可能不足8位)
	//编码与解码时使用
    // 最后一位若为正数,例如可能为0011, 编码记录为3,解码为11,前面2个0丢失
    static Integer lastDigitCapacity = null;

byte[] 封装Node集合

    /**
     * 将byte[] bytes封装为Node集合
     *
     * @param bytes
     * @return
     */
    private static List<Node> getNodes(byte[] bytes) {
   
        List<Node> nodes = new ArrayList<>();

        // 统计每个byte出现的次数
        Map<Byte, Integer> map = new HashMap<>();

        for (byte b : bytes) {
   
            Integer value = map.get(b);
            map.put(b, value == null ? 1 : value + 1);
        }

        map.forEach((k, v) -> {
   
            Node node = new Node(k, v);
            nodes.add(node);
        });

        return nodes;
    }

创建赫夫曼树

    
    private static Node createHuffmanTree(List<Node> nodes) {
   

        while (nodes.size() > 1) {
   
            Collections.sort(nodes);

            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);

            Node parentNode = new Node(null, leftNode.weight + rightNode.weight);
            parentNode.left = leftNode;
            parentNode.right = rightNode;

            nodes.remove(leftNode);
            nodes.remove(rightNode);

            nodes.add(parentNode);
        }

        return nodes.get(0);
    }

生成赫夫曼编码

 private static void getCodes(Node root) {
   
        if (root != null) {
   
            getCodes(root, "", stringBuilder);
        }
    }

    /**
     * 生成哈夫曼编码
     *
     * @param node
     * @param code          左子节点路径0, 右子节点路径1
     * @param stringBuilder
     */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
   
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);

        stringBuilder2.append(code);

        if (node != null) {
   
            if (node.data == null) {
    // 非叶子节点
                // 向左递归
                getCodes(node.left, "0", stringBuilder2);
                // 向右递归
                getCodes(node.right, "1", stringBuilder2);
            } else {
    //叶子结点
                huffmanCodesMap.put(node.data, stringBuilder2.toString());
            }
        }
    }

赫夫曼编码压缩

 /**
     * 赫夫曼编码数据压缩byte[], 按补码编码
     *
     * @param bytes           原始字符串byte[]
     * @param huffmanCodesMap 赫夫曼编码
     * @return 赫夫曼编码后的byte[]
     */
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodesMap) {
   
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
   
            String code = huffmanCodesMap.get(b);
            stringBuilder.append(code);
        }

        // 统计压缩后的byte[]长度
//        int len = (stringBuilder.length() + 7) / 8;
        int len = 0;
        if (stringBuilder.length() % 8 == 0) {
   
            len = stringBuilder.length() / 8;
        } else {
   
            len = stringBuilder.length() / 8 + 1;
        }

        byte[] zipBytes = new byte[len];
        int index = 0; // 记录元素在zipByte的下表位置
        for (int i = 0; i < stringBuilder.length(); i += 8) {
   
            String str;
            // 每8位为一组,进行二进制转换、存储
            // 将赫夫曼编码对应的字符串每8位(补码)进行压缩(十进制数)
            if (i + 8 > stringBuilder.length()) {
   
                // 从当前位置截取到最后一位
                str = stringBuilder.substring(i)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值