算法-16-哈夫曼编码

本文介绍了哈夫曼编码的概念,它是一种无损压缩技术,尤其适用于重复字符多的情况。通过构建哈夫曼树,对字符进行变长编码,以达到数据压缩的目的。文章详细阐述了编码和解码过程,并提供了示例与实际应用。
摘要由CSDN通过智能技术生成

十六、哈夫曼编码(Huffman Coding)

1、概念

它是哈夫曼树(Huffman Tree)在电讯通信领域中的经典应用。广泛应用于数据文件的压缩,压缩率一般在20% ~ 90%之间,属于**可变长编码(VLC)**的一种,重复的内容越多,压缩率越好

但是一个文件如果已经压缩过的,那么再次压缩,其文件大小可能不会有明显的变化

它属于无损压缩

2、原理

定长编码,将信息的原本形式,先转换为字节数组(byte[]),然后再获取每一个字节所对应的二进制(低8位),再进行数据的传输。

变长编码,将信息的原本形式,按照某种规则,进行转换,得到对应的二进制(低8位),再进行数据的传输。

以哈夫曼编码为例,它会将每个字节转换为新的二进制(不一定够8位),即统计每个字节的重复次数重复得越多,其对应的二进制也就越小,并创建一个字典用于映射二进制和字节之间的关系每个二进制都不能是其它二进制的前缀,这也叫前缀编码

3、编码过程

  1. 统计出每个字节的重复次数,作为哈夫曼树节点的权重字节即哈夫曼树节点的值。构造出一棵哈夫曼树(字节重复次数较多的,距离根节点较近)。
  2. 依次遍历每一个节点,遍历左子节点时,添加“0”,遍历右子节点时,添加“1”。最终生成每个叶子节点的前缀编码,和对应的字节-前缀编码映射关系。
  3. 再遍历原信息字节数组,转换为新的对应的前缀编码字节字符串
  4. 最后遍历前缀编码字节字符串,以8位为一个字节,转换为二进制字节元素,添加到字节数组中,最后的几位,如果不足8位,则直接添加每位到字节数组中。这个字节数组,即哈夫曼编码。
  5. 将哈夫曼编码和字节-前缀编码映射关系,一起压缩序列化。

4、解码过程

  1. 先解压反序列化出哈夫曼编码和字节-前缀编码映射关系。
  2. 根据字节-前缀编码映射关系,得到前缀编码-字节映射关系。
  3. 遍历哈夫曼编码(字节数组),将每个字节元素,按位或运算256(补充高位),并取低8位倒数7个字节元素(有一定概率需要补高位,也可以优化算法,记录倒数几个字节元素不需要补充高位),需要判断是否就是字节值,即0和1,如果是,则直接返回。这样得到每个字节元素所对应的前缀编码,拼接起来,即前缀编码字节字符串
  4. 遍历前缀编码字节字符串,根据前缀编码-字节映射关系,得到对应的字节元素,添加到字节数组中,即原信息字节数组。

5、示例

哈夫曼树

class TreeNode implements Comparable<TreeNode> {
   

    private final Byte data;

    private final int weight;

    private TreeNode left;

    private TreeNode right;

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

    @Override
    public int compareTo(TreeNode o) {
   
        return Integer.compare(this.weight, o.weight);
    }

    public Byte getData() {
   
        return data;
    }

    public int getWeight() {
   
        return weight;
    }

    public TreeNode getLeft() {
   
        return left;
    }

    public void setLeft(TreeNode left) {
   
        this.left = left;
    }

    public TreeNode getRight() {
   
        return right;
    }

    public void setRight(TreeNode right) {
   
        this.right = right;
    }
}

编码

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class HuffmanCodeEncoder {
   

    /**
     * 转换为哈夫曼树,并获取根节点
     */
    public TreeNode getTreeRootNode(byte[] bytes) {
   
        Map<Byte, Integer> byteCountMap = new HashMap<Byte, Integer>();
        for (int i = 0, len = bytes.length; i < len; i++) {
   
            byte b = bytes[i];
            Integer count = byteCountMap.get(b);
            if (null == count) {
   
                byteCountMap.put(b, 1);
            } else {
   
                byteCountMap.put(b, count + 1);
            }
        }

        List<TreeNode> list = new ArrayList<TreeNode>(byteCountMap.size());
        for (Map.Entry<Byte, Integer> entry : byteCountMap.entrySet()) {
   
            Byte data = entry.getKey();
            Integer weight = entry.getValue();
            list.add(new TreeNode(data, weight));
        }

        while (1 < list.size()) {
   
            Collections.sort(list);
            TreeNode left = list.get(0);
            TreeNode right = list.get(1);
            TreeNode parent = new TreeNode(null, left.getWeight() + right.getWeight());
            parent.setLeft(left);
            parent.setRight(right);
            list.add(parent);
            list.remove(left);
            list.remove(right);
        }

        return list.get(0);
    }

    /**
     * 获取字节-编码映射关系
     */
    public Map<Byte, String> getByteCodeMap(TreeNode root) {
   
        if (null == root.getLeft() && null == root.getRight()) {
   
            Byte data = root.getData();
            if (null == data) {
   
                return null;
            }

            Map<Byte, String> byteCodeMap = new HashMap<Byte, String>();
            byteCodeMap.put(data, "0");
            return byteCodeMap;
        }

        Map<Byte, String> byteCodeMap = new HashMap<Byte, String>();
        fillByteCodeMap(root, "", "", byteCodeMap);
        return byteCodeMap;
    }

    /**
     * 填充字节-编码映射关系
     */
    private void fillByteCodeMap(TreeNode node, String code, String concat, Map<Byte, String> byteCodeMap) {
   
        if (null == node) {
   
            return;
        }

        code = code.concat(concat);

        fillByteCodeMap(node.getLeft
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值