文章目录

  • 一、赫夫曼树
    • 基本介绍
    • 赫夫曼树几个重要概念和举例说明
  • 赫夫曼树创建步骤图解
  • 代码构建赫夫曼树
  • 二、赫夫曼编码
    • 1基本介绍
    • 通信领域中的信息的处理方式1-定长编码
    • 通信领域中的信息的处理方式2-变长编码
    • 通信领域中信息的处理方式3-赫夫曼编码
    • 将字符串通过赫夫曼进行压缩

一、赫夫曼树

基本介绍

  1. 给定n个权值作为n个叶子结点,构造一颗二叉树。若该树的带权路径长度(wpl)达到最小,这样的二叉树为最优二叉树,也称为哈夫曼树(HuffmanTree)还有的书翻译为霍夫曼树
  2. 赫夫曼树是带权路径长度最短的树,权值较大的节点离根较近

赫夫曼树几个重要概念和举例说明

  1. 路径和路径长度:在一棵树中,从一个节点往下可以达到的孩子或孙子节点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根节点的层数为1,则从根节点到第L层节点的路径长度为L-1

  2. 节点的权及带权路径长度:若将树中节点赋给一个有着某种含义的数值,则这个数值称为该节点的权,节点的带权路径长度为:从根节点到该节点之间的路径长度与该节点的权的乘积

  3. 例如下图,从根节点到13这个节点的路径长度为3-1=2,13这个节点的带权路径长度为2*13=26

  4. 树的带权路径长度:树的带权路径长度规定所有叶子结点的带权路径长度之和,记为WPL(weighted path length)权值越大的节点离根节点越近的二叉树才是最优二叉树

  5. WPL最小的就是赫夫曼树


赫夫曼树创建步骤图解

假如有下面一组数据{13,7,8,3,29,6,1},我们来构建赫夫曼树

  1. 从小到大进行排序,每个数据都是一个节点,每个节点可以看成是一颗最简单的二叉树

    1. 排序{1,3,6,7,8,13,29}
  2. 取出根节点权值最小的两个二叉树,组成一颗新的二叉树,该树的二叉树的根节点的权值是前面两棵二叉树根节点权值的和,比如1,3组成二叉树,二叉树节点权值为1+3=4
  3. 再将这颗新的二叉树,以根节点的权值大小再次排序,不断重复1-2-3-4的步骤,直到数列中,所有的数据都被处理,就得到一颗赫夫曼树

代码构建赫夫曼树

package org.wql.huffmantree;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/**哈夫曼树* Description* User:* Date:* Time:*/
public class HuffmanTree {public static void main(String[] args) {int arr[] = {13,7,8,3,29,6,1};Node root = huffman(arr);preOrder(root);}//创建赫夫曼树的方法public static Node huffman(int[] arr){//遍历arr数组//1.遍历arr数组//2.将arr的每个元素构成一个Node//3.将Node放入到ArrayList中List<Node> nodes = new ArrayList<Node>();for (int value : arr) {nodes.add(new Node(value));}while (nodes.size()>1){//从小到大排序Collections.sort(nodes);//取出根节点权值最小的两颗二叉树Node leftNode = nodes.get(0);Node rightNode = nodes.get(1);Node parent = new Node(leftNode.value+rightNode.value);parent.left=leftNode;parent.right=rightNode;//将原先的两个最小的节点移出集合nodes.remove(leftNode);nodes.remove(rightNode);//将新节点添加入集合nodes.add(parent);}//将赫夫曼树的头节点返回return nodes.get(0);}public static void preOrder(Node root){if(root==null){System.out.println("树空,无法遍历");}else {root.preOrder();}}}//为了让Node对象支持排序
//让Node 实现Comparable<Node>
class Node implements Comparable<Node>{int value;Node left;Node right;public Node(int value){this.value=value;}@Overridepublic String toString() {return "Node{" +"value=" + value +'}';}@Overridepublic int compareTo(Node o) {//表示从小到大排return this.value-o.value;}//前序遍历哈夫曼树public void preOrder(){System.out.println(this);if(this.left!=null){this.left.preOrder();}if(this.right!=null){this.right.preOrder();}}
}

二、赫夫曼编码

1基本介绍

  1. 赫夫曼编码也翻译为哈夫曼编码(Huffman Coding)又称霍夫曼编码,是一种编码方式,属于一种程序算法
  2. 赫夫曼编码是赫夫曼树在电讯通信中的经典的应用之一
  3. 赫夫曼编码广泛的用于数据文件压缩,其压缩率通常在20%~90%之间
  4. 赫夫曼码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,称之为最佳编码

通信领域中的信息的处理方式1-定长编码

如果按照二进制来传递信息,其中长度为359,其中包括空格,可以看到长度非常的长

通信领域中的信息的处理方式2-变长编码

我们通过统计每个字符出现的次数,作为发送依据完成信息传递,长度大大缩减,但是信息读取的精准度却大大下降

通信领域中信息的处理方式3-赫夫曼编码

赫夫曼编码的原理剖析
如果我们要传递这样的字符串:“i like like like java do you like a java”

  1. 首先统计各个字符出现的个数

    1. d:1

      y:1

      u:1

      j:2

      v:2

      o:2

      l:4

      k:4

      3:4

      i:5

      a:5

      :9

  2. 按照上面字符出现的个数构建一颗赫夫曼树,将次数作为权值

  3. 根据赫夫曼树,给每个字符规定编码(前缀编码),向左的路径为0,向右的路径为1

    1. o:1000
    2. u:10010
    3. d:100110
    4. y:100111
    5. i:101
    6. a:110
    7. k:1110
    8. e:1111
    9. j:0000
    10. v:0001
    11. l:001
    12. 空格:01
      按照上方的赫夫曼编码,我们得到字符串对应的编码


可以看到在提高精准度的同时,数据长度也大大减少

注意:

赫夫曼树根据排序方法不同,也可能不太一样,这样对应的赫夫曼编码也不完全一样,但是wpl是一样的,都是最小的

将字符串通过赫夫曼进行压缩

package org.wql.huffmancode;import java.util.*;/*** Description* User:* Date:* Time:*/
public class HuffmanCode {static StringBuilder stringBuilder = new StringBuilder();static Map<Byte,String> huffmanCodes = new HashMap<>();public static void main(String[] args) {String content = "i like like like java do you like a java";byte[] contentBytes = content.getBytes();System.out.println("未压缩之前的长度:"+contentBytes.length);byte[] bytes = huffmanZip(contentBytes);System.out.println("压缩后的结果是:"+Arrays.toString(bytes));System.out.println("压缩率为:"+(double)(contentBytes.length-bytes.length)/contentBytes.length);}public static byte[] huffmanZip(byte[] contentBytes){List<Node> nodes = getNodes(contentBytes);System.out.println(nodes);//生成赫夫曼树Node root = huffman(nodes);preOrder(root);//利用生成的赫夫曼树,完成赫夫曼码表Map<Byte, String> codes = root.getCodes(stringBuilder, huffmanCodes);codes.forEach((v,i)->{System.out.println(v+":"+i);});//通过生成的赫夫曼编码表,测试是否生成了对应的赫夫曼编码byte[] zip = zip(contentBytes, codes);return zip;}//构建赫夫曼树private static Node huffman(List<Node> nodes) {while (nodes.size()>1){Collections.sort(nodes);Node leftNode = nodes.get(0);Node rightNode = nodes.get(1);Node parent = new Node(null, leftNode.weight + rightNode.weight);parent.left=leftNode;parent.right=rightNode;nodes.remove(leftNode);nodes.remove(rightNode);nodes.add(parent);}return nodes.get(0);}public static List<Node> getNodes(byte[] bytes){ArrayList<Node> nodes = new ArrayList<>();Map<Byte,Integer> map = new HashMap<>();for (byte b : bytes) {//count是否为零代表是否已经出现过该字符Integer count = map.get(b);if(count!=null){map.put(b,count+1);}else{map.put(b,1);}}map.forEach((v,i)->{nodes.add(new Node(v,i));});return nodes;}//前序遍历public static void preOrder(Node root){if(root==null){System.out.println("树空,无法遍历");}else {root.preOrder();}}/**** @param bytes 原始字符串对应的byte* @param huffmanCodes huffmanCodes 生成的赫夫曼编码* @return 返回赫夫曼编码处理后的byte[]* 例如返回字符串100101010101010101010001111**huffmanCodeBytes[0]=10010101(补码)*10010101因为是补码,所以我们现将其转为反码再减1* 10010101-1 = 10010100*/private static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){StringBuilder sb = new StringBuilder();for (byte b : bytes) {sb.append(huffmanCodes.get(b));}System.out.println(sb.length());//将字符串转成byte数组//统计返回byte[] huffmanCodeBytes的长度int len = (sb.length()+7)/8;byte[] huffmanCodeBytes = new byte[len];int index = 0;for (int i=0;i<sb.length();i+=8){//每8位对应一个byte,所以步长+8String strByte;if(i+8>sb.length()){strByte = sb.substring(i);}else {strByte = sb.substring(i,i+8);}//将strByte转成byte,放进huffmanCodeByteshuffmanCodeBytes[index] = (byte)Integer.parseInt(strByte,2);index++;}return huffmanCodeBytes;}
}class Node implements Comparable<Node> {Byte data;int weight;Node left;Node right;public Node(Byte data, int weight) {this.data = data;this.weight = weight;}@Overridepublic String toString() {return "Node{" +"data=" + data +", weight=" + weight +'}';}@Overridepublic int compareTo(Node o) {//从小到大排序return this.weight-o.weight;}//前序遍历public void preOrder(){System.out.println(this);if(this.left!=null){this.left.preOrder();}if(this.right!=null){this.right.preOrder();}}public Map<Byte,String> getCodes(StringBuilder stringBuilder,Map<Byte,String> huffmanCodes){StringBuilder builder = new StringBuilder(stringBuilder);if(this.data!=null){huffmanCodes.put(this.data,builder.toString());builder=new StringBuilder("");return huffmanCodes;}if(this.left!=null){builder.append("0");this.left.getCodes(builder,huffmanCodes);}if(this.right!=null){builder.append("1");this.right.getCodes(builder,huffmanCodes);}return huffmanCodes;}
}

一文了解赫夫曼树的构建与赫夫曼编码相关推荐

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

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

  2. 【id:179】【20分】C. DS二叉树--赫夫曼树的构建与编码(不含代码框架)

    题目描述 给定n个权值,根据这些权值构造huffman树,并进行huffman编码 参考课本P147算法6.12 HuffmanCoding代码,注意数组访问是从位置1开始 要求:赫夫曼的构建中,默认 ...

  3. 赫夫曼树介绍、赫夫曼树的性质、赫夫曼编码、赫夫曼树与赫夫曼编码的应用

    文章目录 赫夫曼树 1. 赫夫曼树介绍: 2. 赫夫曼树的创建过程: 3. 赫夫曼树的性质: 4. 赫夫曼编码: 5. 赫夫曼树与赫夫曼编码的c语言代码实现: 赫夫曼树 1. 赫夫曼树介绍: ​ 赫夫 ...

  4. 赫夫曼树的经典应用——赫夫曼编码解码

    之前利用赫夫曼树来对字符串进行压缩,相当于对字符串加密.现在需要利用赫夫曼编码来进行解码,也就是解密. 如果还不了解赫夫曼编码的小伙伴可以先看上篇文章--利用哈夫曼树实现哈夫曼编码进行字符串压缩 之前 ...

  5. 赫夫曼树的创建,赫夫曼编码的原理及使用

    目录 一.创建赫夫曼树 代码实现:最后返回值式创建好的赫夫曼树的顶点 对int[] arr = {13, 7, 8, 3, 29, 6, 1};进行赫夫曼树,我们创建好的node数组依次是这样变化 创 ...

  6. 赫夫曼树赫夫曼编码的创建

    目录 基础知识点 最优二叉树 如何构造赫夫曼树 赫夫曼编码 编码与压缩文件 代码 结构体设计 创建赫夫曼树 创建构建赫夫曼编码 基础知识点 赫夫曼树又称为最优树,是一种带权路径长短最短的树,有着广泛的 ...

  7. 【id:180】【20分】D. DS二叉树--赫夫曼树解码(不含代码框架)

    题目描述 已知赫夫曼编码算法和程序,在此基础上进行赫夫曼解码 在赫夫曼树的类定义中增加了一个公有方法: int  Decode(const string codestr, char txtstr[]) ...

  8. 赫夫曼树以及赫夫曼编码实现

    介绍 赫夫曼树创建图解思路 一直找就找到最好的样子: 赫夫曼树创建代码实现 package 树;import java.util.ArrayList; import java.util.Collect ...

  9. 树结构之--赫夫曼树

    1.什么是赫夫曼树: 给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树 ...

最新文章

  1. 以太网帧,ARP报文
  2. LeetCode Minimum Height Trees(拓扑排序)
  3. 8种常见机器学习算法比较
  4. 笔记本电脑按开机键没反应怎么办?(先记得长按开机键,大约10秒钟看看可以吗)
  5. Failed to bind properties under ‘logging.level‘ to java.util.Map java.lang.String, java.lang.String
  6. 【XAudio2】1.XAudio2 介绍
  7. [转] 哈佛大学研究:一个人成为废物的九大根源
  8. 怎样选择合适的PoE交换机?POE交换机选用小常识!
  9. laravel 关联关系之多态关联
  10. tx1调试车辆检测遇到的问题
  11. Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8
  12. 手机木马以及移动安全
  13. 表白生成器PHP源码,表白网页在线生成源码
  14. Latex特殊符号波浪号~,^
  15. 文章标题一个应届计算机毕业生的2012求职之路
  16. 标题:缩位求和 在电子计算机普及以前,人们经常用一个粗略的方法来验算四则运算是否正确。 比如:248 * 15 = 3720 把乘数和被乘数分别逐位求和,如果是多位数再逐位求和,直到是1位数,得 2
  17. 易语言助手用法与方便之处
  18. Learning Cocos2d-x for WP8(2)——深入刨析Hello World
  19. ffmpeg转码时对变帧率和固定帧率的处理
  20. Android(安卓)简单拨号器案例

热门文章

  1. python爬网易新闻_爬虫基本介绍 python3 爬虫爬取网易新闻排行榜
  2. 艾美捷ProSci丨ProSci B7-H3 免疫检查点解决方案
  3. 深情回眸 经典软件十五年风雨兼程
  4. IBS2.0绘制蛋白质,核酸结构
  5. python读取文件,jieba分词,posseg标注词性,并写入文件,代码实战
  6. 移动光猫 烽火HG6145F 获取管理员密码 启用USB存储功能
  7. Java入门基础知识点整理大放送,推荐保存
  8. Compilation failed:internal java compiler error错误解决
  9. 微信小程序4-视图与逻辑
  10. 美团民宿跨端复用框架设计与实践