哈夫曼编码

2022-05-09 21:12:25 浏览数 (1)

离开了校园,连手机流量都要省着用,然后突发奇想的思考如何才能节省传输大小

1. 哈夫曼树

构建最短带权路径长度的二叉树,叫做哈夫曼树,也叫最优树(权重越大的结点离树根越近)

1.1 基本定义

  • 路径:树中的一个节点到另一个节点之间的通路
  • 路径长度:某路径中所经过的节点数量
  • 节点的权:给节点赋值,这个值称为节点的权
  • 节点的带权路径长度:根节点到某节点的路径长度 * 该节点的权
  • 树的带权路径长度:树中所有叶子节点的带权路径长度之和,记作 "WPL"

1.2 构建步骤

  • 选出两个最小权值作为左右子树,新建其父节点(虚线),权值为左右子树权值之和
  • 从权值队列中删除第一步的左右子树的权值,添加第一步新增的二叉树到权值队列
  • 重复第一、二步,直到构建完权值队列中所有节点

1.3 构建图示

WPL:9 * 1 6 * 2 4 * 3 1 * 3 = 36

2. 哈夫曼编码

哈夫曼编码是一种编码方式,其可以对信息进行压缩,而从提高存储,传输的效率

2.1 基本定义

  • 等长编码:任何字符的编码长度都相同,比如ASCII。虽读写方便,但浪费资源,且有前缀码问题
  • 无前缀编码:任一个编码都不是其他任何编码的前缀。[01,10,11,100,101]中10是100的前缀,因此不是无前缀编码

2.2 构建步骤

  • 根据权值构建哈夫曼树
  • 将哈夫曼树的左树标 0,右树标记1,根节点不计算
  • 将权值替换为对应的字符
  • 列出字符对应的二进制

2.3 构建图示

假设字符A、B、C、D对应的权值为1、9、4、6

(4)

字符

编码

A

000

B

1

C

001

D

01

2.4 哈夫曼编码应用

通过哈夫曼编码传输文本、图片,查看前后对比

2.4.1 哈夫曼编码 java 实现
代码语言:javascript复制
/**
 * @author Howl
 * 哈夫曼编码
 */
public class HuffmanCode {
    /**
     * 哈夫曼树结点结构
     */
    private static class Node implements Comparable<Node> {
        /**
         * 权值、二进制编码0、1、左右孩子
         */
        int weight;
        String code;
        Node left;
        Node right;

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

        Node(int weight, Node left, Node right) {
            this.weight = weight;
            this.left = left;
            this.right = right;
        }

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

    /**
     * 根节点、存储全部节点
     */
    private Node root;
    private static Node[] nodes;

    /**
     * 构建哈夫曼树
     * 建树是从最小权值开始的,所以借助小根堆
     */
    public void createHuffman(int[] weights) {

        // 构建小根堆,弹出是最小的元素
        Queue<Node> nodeQueue = new PriorityQueue<>();
        nodes = new Node[weights.length];
        for (int i = 0; i < weights.length; i  ) {
            nodes[i] = new Node(weights[i]);
            nodeQueue.add(nodes[i]);
        }

        // 节点队列只剩一个节点结束,即根节点构建完成
        while (nodeQueue.size() > 1) {

            // 弹出权值最小的两个结点
            Node left = nodeQueue.poll();
            Node right = nodeQueue.poll();

            // 创建新节点作为两结点的父节点
            Node parent = new Node(left.weight   right.weight, left, right);
            nodeQueue.add(parent);
        }

        // 哈夫曼树根,遍历要用,优先级队列GC
        root = nodeQueue.poll();
        // 实现编码
        encode();
    }

    /**
     * 输入字符下标,输出对应的哈夫曼编码
     */
    public String convertHuffmanCode(int index) {
        return nodes[index].code;
    }

    /**
     * 递归填充二进制编码
     */
    private void encode() {
        encode(root, "");
    }

    private void encode(Node node, String code) {
        if (node == null) {
            return;
        }
        node.code = code;
        encode(node.left, node.code   "0");
        encode(node.right, node.code   "1");
    }

    /**
     * 测试
     */
    public static void main(String[] args) {

        // 字符及其对应权值
        char[] chars = {'A', 'B', 'C', 'D', 'E', 'F'};
        int[] weights = {2, 3, 7, 9, 18, 25};

        HuffmanCode huffManCode = new HuffmanCode();
        huffManCode.createHuffman(weights);
        for (int i = 0; i < chars.length; i  ) {
            System.out.println(chars[i]   ":"   huffManCode.convertHuffmanCode(i));
        }
    }
}
2.4.2 传输实现
  • 将原文进行哈夫曼编码,记录字符及其对应的编码,保存文件为 HuffmanCode
  • 将原文的字符用哈夫曼编码代替,保存文件为 HuffmanText
  • 将上面两个文件发送给对方
  • 对方根据这两份文件就可以解码出原文

3. 开源压缩框架

3.1 图片

Thumbnailator是专门压缩图片的,使用非常简洁

代码语言:javascript复制
// 图片压缩
Thumbnails.of("C:\Users\Howl\Desktop\InPic.png")	// 输入图片地址
    .scale(1)											// 和原尺寸大小,即长度,1是原大小
    .outputQuality(0.5)									// 和原图的质量
    .outputFormat("jpg")								// 输出格式,png为高保真不会压缩
    .toFile("C:\Users\Howl\Desktop\OutPic");		// 输出地址
}

参考:

程序员小灰

0 人点赞