思考两个问题

  1. 电报发送:二战的时候大家都知道那时候普遍会应用电报,如果让你来设计一个电报的发送编码你该如何设计呢?
  1. 电报加密后越短越好,发送快。
  2. 破解难
  3. 解码容易
  4. 换加密树也要快
  5. 可逆的
  1. 压缩算法:给你10000个字符(每个字符1btye,也就是8bit)的文件,你怎么存储可以尽可能的节省空间呢?假设字符是 a b c d 4种,10000 * 8 =80000bit

    思路1:重复的去掉;思路2:使用二进制代替

假定 a=000 b=001 c=010 d=100,这样我们每个字符由于原来8bit就变成了3bit的二进制,缩小了将近3倍
举例分析:dab二进制为100000001,若是abcdaaa二进制为000001010100000000000,

出现很多重复的a是否可以进一步优化?
假设优化:A:0 B:001 C:010 D:100,则abcdaaa:0001010100000=>abcdaaa

进一步将重复的数据缩小了,是否存在问题?
abcdaaa解码,以前3个bit表示一个字符,现在b、c编码包含a,不支持解码,怎么优化?

优化思路:区分开a和b,赫夫曼编码(前缀编码)
优化:A:0 B:101 C:110 D:100,则abcdaaa编码0101110100000

解码伪代码如下

// A:0       B:101    C:110    D:100
Map mapping;
String code="01011101000000000"
var decode;
var currDecode;
for(int i=0;i<code.lenth;i++){currDecode+=char(i);var res = mapping.get(currDecode);if(res != null){decode+=res}
}

最优二叉树(哈夫曼树)

| 满二叉树 | 除了叶子节点,其他的都有两个子节点,1 2 4 8这样的节点 2^n个点
|
| — | — |
| 完全二叉树 | 除了最底层都有两个子节点,而且叶子节点是靠左连续的
|


计算这三颗二叉树的带权路径长度总和:其中每个点的权重为:a:7 b:5 c:2 d:4
公式:每个节点深度*权重的和

WPL(a):72+52+22+42=36()
WPL(b):73+53+21+42=46()
WPL©:71+52+23+43=35()
发现什么? 权重越大节点,深度越低,和越小,和最小的就是最优二叉树

定义:

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近

那么这个赫夫曼树和压缩又有什么关系呢?

二叉树,二叉,这时候你要想到二进制,二叉分左右嘛。左节点的边设置为0,右节点的边设置为1

哈夫曼编码定义:

最优二叉树中我们给每一条边加上一个权值,指向左子节点的边我们标记为0,指向右子节点的边标记为1,那从根节点到叶节点的路径就是我们说的哈夫曼编码

所以图c的赫夫曼树对应的编码就是:
a:0 b:10 c:110 d:111

构建思路

核心思想:贪心算法:利用局部最优推出全局最优,把频率出现多的用短码表示,频率出现小的就用长一点。而且,任何一个字符的编码都不是另一个的前缀,在解压缩的时候,我们每次会读取尽可能长的可解压的二进制串,所以在解压缩的时候也不会产生歧义

具体实现思路:

  1. 按权重排序
  2. 每次取数值最小的两个节点,将之组成为一颗子树。
  3. 移除原来的两个点
  4. 然后将组成的子树放入原来的序列中
  5. 重复执行1 2 3 直到只剩最后一个点

实现哈夫曼树

例子: a:3 b:24 c:6 d:20 e:34 f:4 g:12,根据以上权重来实现哈夫曼树

构建哈夫曼树

package datastructure.tree;import java.util.*;/*** 哈夫曼树** @author zw* @create 2023-04-09 1:24*/
public class HuffmenTree<T extends Comparable<T>> {MyTreeNode<T> root;List<MyTreeNode<T>> leafs;                // 叶子节点Map<T, Integer> weights; // 叶子节点的权重, a,b,c,d,eMap<String, String> encodeMap = new HashMap<>(); // 编码表Map<String, String> decodeMap = new HashMap<>(); // 解码表public HuffmenTree(Map<T, Integer> weights) {this.weights = weights;leafs = new ArrayList<>();}/*** 构建哈夫曼树*/public void builder() {// 按权重排序,这可以用之前写得排序算法,为了方便使用jdk的优先队列PriorityQueue<MyTreeNode> priorityQueue = new PriorityQueue<MyTreeNode>((o1, o2) -> o1.weight - o2.weight);weights.forEach((data, weight) -> {MyTreeNode MyTreeNode = new MyTreeNode(data, weight);priorityQueue.add(MyTreeNode);leafs.add(MyTreeNode);});MyTreeNode root = null;while (priorityQueue.size() != 1) {// 从优先队列拿出最小的两个节点合并MyTreeNode leftNode = priorityQueue.poll();MyTreeNode rightNode = priorityQueue.poll();// 将合并节点放入优先对垒int parentNodeWight = leftNode.weight + rightNode.weight;MyTreeNode<T> parentNode = new MyTreeNode<T>(null, parentNodeWight);parentNode.left = leftNode;parentNode.right = rightNode;leftNode.parent = parentNode;rightNode.parent = parentNode;priorityQueue.add(parentNode);root = parentNode;}// 将根节点返回this.root = root;}/*** 解码* @param code* @return*/public String decode(String code) {String decode = "";char[] chars = code.toCharArray();String currCode = "";for (int i = 0; i < chars.length; i++) {currCode += chars[i];if (decodeMap.containsKey(currCode)) {String data = decodeMap.get(currCode);decode += data;currCode = "";}}return decode;}/*** 对输入编码** @param input*/public String encode(String input) {String code = "";for (char data : input.toCharArray()) {String encode = encodeMap.get(String.valueOf(data));code += encode;}return code;}/*** 对叶子节点编码** @return*/public void leafencode() {// 两种方式:1、自顶向下  2、自底向上,这儿采用自底向上for (MyTreeNode<T> leaf : leafs) {String code = "";T data = leaf.data;MyTreeNode currNode = leaf;while (currNode.parent != null) {// 判断当前节点,是左是右MyTreeNode parentNode = currNode.parent;if (parentNode.left == currNode) {code = "0" + code; // 自底向上编码是反的,这儿处理下} else {code = "1" + code;}currNode = currNode.parent;}encodeMap.put(data.toString(), code); // 初始化编码表decodeMap.put(code, data.toString());  // 初始化解码表}}
}

测试用例

    public static void main(String[] args) {// a:3 b:24 c:6 d:20 e:34 f:4 g:12Map<Character, Integer> weights = new HashMap<Character, Integer>();weights.put('a', 3);weights.put('b', 24);weights.put('c', 6);weights.put('d', 20);weights.put('e', 34);weights.put('f', 4);weights.put('g', 12);HuffmenTree huffmenTree = new HuffmenTree(weights);huffmenTree.builder();huffmenTree.leafencode();huffmenTree.root.show();String input = "aceg";String encode = huffmenTree.encode(input);String decode = huffmenTree.decode(encode);String log = String.format("编码表:%s\n解码表:%s\n输入:%s\n编码结果:%s\n解码结果:%s",huffmenTree.encodeMap,huffmenTree.decodeMap,input,encode,decode);System.out.println(log);}

运行结果

                                                null                                             /            \                                           null                null                                   /       \           /       \                                  d            b      null         e                              /         \                                           g           null                                     /  \                                      c       null                                 / \                                   a   f
编码表:{a=10110, b=01, c=1010, d=00, e=11, f=10111, g=100}
解码表:{00=d, 11=e, 01=b, 100=g, 10110=a, 1010=c, 10111=f}
输入:aceg
编码结果:10110101011100
解码结果:acega   f
编码表:{a=10110, b=01, c=1010, d=00, e=11, f=10111, g=100}
解码表:{00=d, 11=e, 01=b, 100=g, 10110=a, 1010=c, 10111=f}
输入:aceg
编码结果:10110101011100
解码结果:aceg

高级数据结构之赫夫曼树相关推荐

  1. 2020-10-1 //严蔚敏《数据结构》 //赫夫曼树及其应用:创建顺序赫夫曼树创建及得到赫夫曼编码

    //严蔚敏<数据结构> //赫夫曼树及其应用:创建顺序赫夫曼树创建及得到赫夫曼编码 //(从叶子结点到根逆向求每个字符的赫夫曼编码)以及(无栈非递归遍历赫夫曼树,求赫夫曼编码) //自学中 ...

  2. 【数据结构】赫夫曼树

    数据结构赫夫曼树 /*名称:赫夫曼树语言:数据结构C语言版 编译环境:VC++ 6.0日期: 2014-3-26 */#include <stdio.h> #include <lim ...

  3. 【Java数据结构】赫夫曼树

    哈弗曼树 哈弗曼树定义 哈弗曼树示例 哈弗曼树代码实现 哈弗曼树定义 给定 N 个权值作为 N 个叶子结点,构造一棵二叉树,若该树的带权路径长度(WPL)达到最小,称这样的二叉树为最优二叉树,也称为哈 ...

  4. 【数据结构】赫夫曼树与编码

    赫夫曼树与赫夫曼编码 前言 赫夫曼树 存储结构 初始化树 构建树 赫夫曼编码 初始化编码 构建编码 前言 (概念) 路径:从一个节点到另一个节点的分支 路径长度:从一个节点到另一个节点的分支总数 节点 ...

  5. 【数据结构】赫夫曼树与赫夫曼编码(可执行完整代码)

    赫夫曼编码对文件进行压缩与解密 理论 赫夫曼树 赫夫曼编码 应用 应用源码 运行结果截图 理论 赫夫曼树 先有赫夫曼树,才有赫夫曼编码.所以,首先简单介绍一下什么是赫夫曼树. 假设一共五个叶子节点,分 ...

  6. 【赫夫曼树数据结构及其应用】

    本文主要介绍Java中赫夫曼树数据结构的基本原理.实现方式以及使用场景.赫夫曼树(Huffman Tree)是一种带权路径最短的二叉树,广泛应用于数据压缩和编码等领域. 一.赫夫曼树的基本概念 赫夫曼 ...

  7. 数据结构(十五)— 树结构之赫夫曼树及其应用

    现在我们都是讲究效率的社会,什么都要求速度, 在不能出错的情况下,做任何事情都讲究越快越好.在计算机和互联网技术中,文本压缩就是一个非常重要的技术. 玩电脑的人几乎都会应用压缩和解压缩软件来处理文档. ...

  8. 数据结构--赫夫曼树

    数据结构 –赫夫曼树 文章目录 数据结构 一.一些概念 二.最优二叉树(赫夫曼树) 三.赫夫曼树的构造 四.赫夫曼编码 五.前缀编码 一.一些概念 路径:从树中一个结点到另一个结点之间的分支构成这两个 ...

  9. java振动数据压缩_【数据结构-Java】最佳实践-数据压缩(使用赫夫曼树)

    一.需求 将给出的一段文本,比如 "i like like like java do you like a java" , 根据前面的讲的赫夫曼编码原理,对其进行数据压缩处理 二. ...

最新文章

  1. R语言distMeeus函数计算大圆距离实战(Great Circle Distance)
  2. 助力南京银行打造国内首个分布式核心业务系统
  3. 兔子--eclipse设置编码格式
  4. LFS、BLFS、ALFS、HLFS的区别
  5. C++ trivial和non-trivial构造函数及POD类型(转)
  6. Hyperledger Fabric 管道(2) 管道的操作
  7. file.seek()/tell()-笔记
  8. uiw 1.2.17 发布,基于 React 16 的组件库
  9. 吃糖果(信息学奥赛一本通-T1193)
  10. Python 函数式编程,从入门到放弃
  11. [转载] Python编程之np.argmax()的用法
  12. wireshark_Couldn’t run /usr/sbin/dumpcap in child process: Permission denied
  13. 阶段3 1.Mybatis_09.Mybatis的多表操作_6 分析mybatis多对多的步骤并搭建环境
  14. 流畅的python读书笔记-第十章-序列的修改、散列和切片
  15. 10种软件滤波方法的示例程序
  16. Win8 64位 安装 Delphi5
  17. getc()、gets()、getchar()、scanf()的区别
  18. App Ratings and Reviews
  19. 基于stc51单片机的指纹解锁模块
  20. 区块链核心概念(1)

热门文章

  1. 代码覆盖率 ——语句覆盖 Statement Coverage、分支覆盖 Branch Coverage、 路径覆盖 Path Coverage的区别
  2. Centos逻辑卷扩容、合并
  3. 软件项目管理文档模板目录
  4. tomcat官网如何下载低版本的tomcat
  5. 古月居ROS入门21讲笔记
  6. Java开发人员幽默外号,姓李的幽默外号 - 经典语录大全
  7. 算法设计与分析——算法基础初步了解
  8. mysql 1701,MySQL ERROR 1701 (42000)
  9. 知识图谱初步学习(零)——本体是什么
  10. 数据结构题目收录(一)