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

数据压缩----霍夫曼树和霍夫曼压缩

作者头像
SuperHeroes
发布2018-05-30 18:03:40
6870
发布2018-05-30 18:03:40
举报
文章被收录于专栏:云霄雨霁云霄雨霁

霍夫曼压缩的思想:使用较少的比特表示出现频繁的字符而使用较多的比特表示使用较少的字符。这样表示字符串所使用的总比特数就会减少。

前提:所有字符编码都不会成为其他字符编码的前缀。使用霍夫曼树可以保证这个前提的成立。

构造霍夫曼树:

首先定义霍夫曼树的结点类

代码语言:javascript
复制
private static class Node implements Comparable<Node> {
    private final char ch;
    private final int freq;
    private final Node left, right;

    Node(char ch, int freq, Node left, Node right) {
        this.ch    = ch;
        this.freq  = freq;
        this.left  = left;
        this.right = right;
    }
    private boolean isLeaf() { return (left == null) && (right == null); }
    public int compareTo(Node that) { return this.freq - that.freq; }
}

然后构建霍夫曼树:

霍夫曼树是一个二轮算法,它需要扫描目标字符串两次才能压缩它。第一次扫描统计每个字符出现的频率,第二次扫描根据生成的编译表压缩。

构造过程如下:为每个字符创建一个独立的结点(可以看成只有一个结点的树)。首先找到两个频率最小的结点,然后创建一个以这两个结点为子结点的新节点(新节点的频率值为两个子结点频率值之和);这个操作会使森林中树的数量减一。不断重复这个过程直到只剩下一棵树为止。这样,从树的根结点到叶结点的路径就是叶结点中字符对应的霍夫曼编码。

代码语言:javascript
复制
 private static Node buildTrie(int[] freq) {
    MinPQ<Node> pq = new MinPQ<Node>();
    for (char i = 0; i < R; i++)
        if (freq[i] > 0)
            pq.insert(new Node(i, freq[i], 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();
}

解码操作:

根据霍夫曼树将比特流解码:根据比特流的输入从根节点向下移动(遇到0走左子结点,遇到1走右子结点),遇到叶结点后,输出该叶结点的字符并回到根结点。

代码语言:javascript
复制
 public static void expand() {
    Node root = readTrie(); 
    int length = BinaryStdIn.readInt();

    for (int i = 0; i < length; i++) {
        Node x = root;
        while (!x.isLeaf()) {
            boolean bit = BinaryStdIn.readBoolean();
            if (bit) x = x.right;
            else     x = x.left;
        }
        BinaryStdOut.write(x.ch);
   }
   BinaryStdOut.close();
}

压缩操作:

压缩操作是根据构造的编译表实现的。根据霍夫曼树建立一张字符和路径对应的二进制字符串相联系的表,然后扫描目标字符串,每读入一个字符,查表得到相应的二进制字符串并输出即可。

构建编译表:

代码语言: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
复制
for (int i = 0; i < input.length; i++) {
        String code = st[input[i]];
        for (int j = 0; j < code.length(); j++) {
            if (code.charAt(j) == '0')
                BinaryStdOut.write(false);
            else (code.charAt(j) == '1') 
                BinaryStdOut.write(true);
        }
    }

对于任意前缀码,编码后的比特字符串长度等于霍夫曼树的加权外部路径长度。

给定一个含有r个符号的集合和它们的频率,霍夫曼算法所构造的前缀码是最优的。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 构造霍夫曼树:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档