前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >霍夫曼压缩算法

霍夫曼压缩算法

作者头像
felix
发布2018-06-08 11:26:29
1.6K0
发布2018-06-08 11:26:29
举报
文章被收录于专栏:Felix的技术分享Felix的技术分享

霍夫曼压缩算法

概述

霍夫曼压缩算法的主要思想是用较少的比特表示出现频率较高的字符,用较多的比特表示出现频率较低的字符。如下图所示,

实现

代码语言:javascript
复制
①读入完整的输入流,并转化为字符数组。
②计算每个字符出现的次数
③构建Huffman树
④构建编译表
⑤将单词查找树编码成比特输出串并写入到输出流
⑥将单词总数编码成比特输出串并写入到输出流
⑦使用编译表翻译每个输入字符

节点的表示

代码语言:javascript
复制
    private static final int R = 256;       //字符为ASCII表示

    private static class Node implements Comparable<Node> {

        private final char ch;
        private final int freq;
        private final Node left, right;

        public Node(char ch, int freq, Node left, Node right) {
            this.freq = freq;
            this.ch = ch;
            this.left = left;
            this.right = right;
        }

        private boolean isLeaf() {
            return left == null && right == null;
        }

        @Override
        public int compareTo(Node that) {
            return this.freq - that.freq;
        }
    }

构建Huffman单词查找树

构建初始有一堆没有父节点的节点,将它们放到最小队列中,这样对头总是freq为最小的那个节点。 从队列中找到freq最小的两个节点,创建一个它们的父节点,将三个节点归并成一个大节点,接着放入队列中, 循环往复直至队列中只剩一个节点。

代码语言:javascript
复制
    /**
     * @param freq 字符出现的次数
     * @return
     */
    private static Node buildTrie(char[] freq) {
        MinPQ<Node> pq = new MinPQ<Node>();

        //初始化多个将构成一颗Huffman树的结点
        for (char i = 0; i < R; i++) {
            if (freq[i] > 0) pq.insert(new Node(i, freq[i], null, null));
        }

        // special case in case there is only one character with a nonzero frequency
        if (pq.size() == 1) {
            if (freq['\0'] == 0) pq.insert(new Node('\0', 0, null, null));
            else pq.insert(new Node('\1', 0, null, null));
        }

        //归并两个小树
        while (pq.size() > 1) {
            Node left = pq.delMin();
            Node right = pq.delMin();

            Node parent = new Node('\0', left.freq + right.freq, left, right);      //创建连接子树的中间结点
            pq.insert(parent);
        }

        return pq.delMin();
    }

将Huffman单词查找树转化成字节流写到压缩文件中

做如下规定: 中间结点写0;叶子结点写1,并在后面写结点上的字符。

代码语言:javascript
复制
    /**
     * 将单词查找树编码成比特输出串并写入到输出流
     * 用前序遍历
     */
    private static void writeTrie(Node x) {
        if (x.isLeaf()) {
            BinaryStdOut.write(true);
            BinaryStdOut.write(x.ch);
            return;
        }

        BinaryStdOut.write(false);
        writeTrie(x.left);
        writeTrie(x.right);
    }

将压缩文件中字节流转化为Huffman单词查找树

按写入时的规定解析字节流。

代码语言:javascript
复制
    /**
     * 读比特流,得出一颗单词查找树
     */
    private static Node readTrie() {
        if (BinaryStdIn.readBoolean()) {   //读到1,说明是叶子结点
            return new Node(BinaryStdIn.readChar(), 0, null, null);
        }

        //读到的是0,说明是中间结点,需要递归直到读到1为止

        Node left = readTrie();
        Node right = readTrie();
        return new Node('\0', 0, left, right);
    }

构建编译表

构建编译表st,索引为字符,值为路径(比特字符串)。 根据这张表,可以将源文件中的某个字符,压缩为更少bit表示的Huffman树上的路径。

代码语言:javascript
复制
    private static void buildCode(String[] st, Node x, String s) {
        if (!x.isLeaf()) {
            buildCode(st, x.left, s + "0");
            buildCode(st, x.right, s + "1");
        } else {
            st[x.ch] = s;
        }

    }

压缩

代码语言:javascript
复制
      /**
     * 从输入流中读字节流,并将压缩后的结果写入输出流
     */
    private static void compress() {
        //①读入完整的输入流,并转化为字符数组
        String s = BinaryStdIn.readString();
        char[] input = s.toCharArray();

        //②计算每个字符出现的次数,没有出现的就为0
        char[] freq = new char[R];
        for (int i = 0; i < input.length; i++) {
            freq[input[i]]++;
        }

        //③构建Huffman树
        Node root = buildTrie(freq);

        //④构建编译表,将输入中的每个char值与一个比特字符串(即Huffman树上路径)相关联
        String st[] = new String[R];
        buildCode(st, root, "");


        //⑤将单词查找树编码成比特输出串并写入到输出流
        writeTrie(root);

        //⑥将单词总数编码成比特输出串并写入到输出流
        BinaryStdOut.write(input.length);


        //⑦使用编译表翻译每个输入字符
        for (int i = 0; i < input.length; i++) {
            String code = st[input[i]];   //code表示Huffman单词查找数上的路径
            for (int j = 0; j < code.length(); j++) {  //要一位一位地输出
                if (code.charAt(j) == '1') {
                    BinaryStdOut.write(true);
                } else {
                    BinaryStdOut.write(false);
                }
            }

        }

        BinaryStdOut.close();
    }

### 解压

代码语言:javascript
复制
     /**
     * 解压
     * 读取压缩文件的比特流,
     * 将比特流对应为路径在单词查找树上找,将找到的结点中的字符写出
     */
    private static void expand() {
        Node root = readTrie();
        int N = BinaryStdIn.readInt();  //读出存在压缩文件中的字符串长度

        for (int i = 0; i < N; i++) {   //找出源文件中每个字符

            Node x = root;
            while (!x.isLeaf()) {       //遍历,知道叶子结点
                if (BinaryStdIn.readBoolean()) {
                    x = x.right;
                } else {
                    x = x.left;
                }

            }

            BinaryStdOut.write(x.ch);
        }

        BinaryStdOut.close();
    }

参考

Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015年05月05日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 霍夫曼压缩算法
    • 概述
      • 实现
        • 节点的表示
        • 构建Huffman单词查找树
        • 将Huffman单词查找树转化成字节流写到压缩文件中
        • 将压缩文件中字节流转化为Huffman单词查找树
        • 构建编译表
        • 压缩
      • 参考
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档