ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

6.4 赫夫曼树

2022-01-28 18:58:36  阅读:171  来源: 互联网

标签:字符 二义性 编码 6.4 frequency forest 夫曼


  赫夫曼树是一棵压缩树。其基本理论是先统计字符串里的字符出现频率,然后用短码替换频率高的字符,用长码替换频率低的字符。
  但是要实现必须满足两个要求:
  一 替换码不能重复
  二 没有二义性
  赫夫曼选择了树来实现。赫夫曼树只用叶子来代表字符。以到根节点的路径来表示编码。左路径用0,右路径用1表示。
  这可以很轻松地理解替换码不重复。因为到根节点的路径是唯一的。
  下图是一个典型的赫夫曼树
在这里插入图片描述

  但是无二义性怎么理解呢?
  把上述赫夫曼树的编码显示出来是这个样子:
在这里插入图片描述

  无二义性是靠只用叶子来表示字符决定的。
  比如上图的s,编码是0。因为s是叶子,所以不会出现00和01这样的编码。所以就不存在二义性。
  假如00 表示字符A,而0又表示字符B。那么就出现了二义性。当出现00时,是两个B,还是一个A呢?
  理解了了重复和二义性后,赫夫曼树最难的理论知识是长短码问题。
  赫夫曼树每个节点都有权重字段。对于每个叶子节点来说,权重就是它代表的字符的出现次数。
  如上图,M出现1次,那么权重就是1。
  M和p加起来权重是3,才仅仅和i同级。
  所以赫夫曼树是以权重相加来构建层级关系的。
  那么具体的构建过程是怎么样的?
  需要用到两种数据结构,一是堆,二是赫夫曼树。
  其构建过程如下:
  一 统计出现频率,可以使用hashmap来实现。
  二 将所有字符和对应权重构建成单root节点赫夫曼树,放入堆中。
  三 每次从堆中取出最小权重的两棵树,权重相加合并成新的赫夫曼树之后放回堆中,直到堆中只有一棵树。
  四 遍历赫夫曼树,将所有叶子的编码以map存储。
  五 将原字符串替换为赫夫曼编码。
  经过这五步,压缩完成。
  代码如下:

public HuffmanTree(String text) {
    // 首先计算出各个字母出现的频率
    final char[] chars = text.toCharArray();
    this.frequency = new HashMap<>();
    for (char c : chars) {
        if (frequency.containsKey(c)) {
            frequency.put(c, frequency.get(c) + 1);
        } else {
            frequency.put(c, 1);
        }
    }
    // 组成一片森林,加入到优先队列里
    final PriorityQueue<HuffmanTree> forest = new PriorityQueue<>();
    frequency.forEach((key, value) -> {
        forest.add(new HuffmanTree(key, value));
    });
    while (forest.size() > 1) {
       // 取出两个来然后加回去
        final HuffmanTree first = forest.poll();
        final HuffmanTree second = forest.poll();
        HuffmanTree merged = merge(first, second);
        forest.add(merged);
    }

    codeMap = new HashMap<>();
    this.root = forest.poll().root;

    this.bfs(x -> {
        HuffmanNode node = (HuffmanNode)x;
        if (((HuffmanNode) x).getElement() == null) {
            return true;
        }
        codeMap.put(node.getElement(), node.getFullCode());
        return true;
    });
    StringBuilder stringBuilder = new StringBuilder();
    for (char c : chars) {
        final String s = codeMap.get(c);
        stringBuilder.append(s);
    }
    this.result = stringBuilder.toString();
}

  因为仅仅是演示赫夫曼编码原理,所以示例代码中没有将结果的01系列变成byte数组,而是把0和1的系列放入字符串中。
  例如Mississippi
  编码结果是100110011001110110111
  原字符串是88 bit,压缩后是21bit。压缩比接近四分之一,可见赫夫曼树算法的优越性。

标签:字符,二义性,编码,6.4,frequency,forest,夫曼
来源: https://blog.csdn.net/m0_66201040/article/details/122735239

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有