霍夫曼树基本概念:

路径:从一个结点往下到孩子或孙子结点之间的同理

路径长度:如结点1到结点7的路径长度=2

结点的权:将结点的某一属性值作为结点的权

带权路径长度:从根节点到该结点*该结点的权;如结点1到结点7的带权路径长度:7*2=14

的带权路径长度(WPL):该树的所有叶子结点的带权路径长度之和

霍夫曼树:给定n个权值,构造一颗二叉树并由这n个权值作为数的叶子结点,且该树的带权路径长度(WPL)达最小,这样的二叉树成为最优二叉树,也叫霍夫曼树

霍夫曼树特点:权值越大的叶子结点离根节点越近

霍夫曼编码:

编码规则:

(1)给定一个字符串,统计各个字符出现的次数,将次数作为权值构成霍夫曼树;例如“i like like like java do you like a java”转化为霍夫曼树为:

(2)规定路径向左为0,向右为1,则各个权值的路径即为他们的霍夫曼编码

注意:

(1)霍夫曼编码为前缀编码,即任何编码不会是其他编码的前缀(因为叶子结点)

(2)若出现权值相同的结点,则根据排序方法不同,对应的霍夫曼编码也不完全相同,但压缩率是相同的。

代码实现:

以“i like like like java do you like a java”为例

结点

class ByteNode implements Comparable<ByteNode> {Byte data;//存放字符本身,注意用包装类方便存入集合中int weight;//权值,表示字符出现的次数ByteNode left;ByteNode right;public ByteNode(Byte data, int weight) {this.data = data;this.weight = weight;}@Overridepublic int compareTo(ByteNode o) {return this.weight - o.weight;}@Overridepublic String toString() {return "ByteNode{" +"data=" + data +", weight=" + weight +'}';}//前序遍历public void preOrder() {System.out.println(this);if (this.left != null)this.left.preOrder();if (this.right != null)this.right.preOrder();}
}

字符串->生成结点并放入List中

 private static List<ByteNode> getList(String str) {byte[] bytes=str.getBytes();//转换为byte数组,得到一个个字符HashMap<Byte, Integer> counts = new HashMap<>();//统计字符+次数,需要Map实现//遍历bytes,统计每个byte出现的次数,存放到Hashmap中for (byte b : bytes) {Integer count = counts.get(b);//get(key),返回valueif (count == null)counts.put(b, 1);elsecounts.put(b, count + 1);//如果放入相同的key,则新的值会替换旧的}//将Map保存的字符+次数生成结点,并存放到List<ByteNode>中ArrayList<ByteNode> nodesList = new ArrayList<>();for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {//遍历map,将node结点加入到list中nodesList.add(new ByteNode(entry.getKey(), entry.getValue()));}return nodesList;}

输出:

List->霍夫曼树

//list生成霍夫曼树private static ByteNode getHuffManTree(List<ByteNode> list) {while (list.size() > 1) {Collections.sort(list);ByteNode leftNode = list.get(0);ByteNode rightNode = list.get(1);ByteNode parent = new ByteNode(null, leftNode.weight + rightNode.weight);//注意父节点都设为nullparent.left = leftNode;parent.right = rightNode;list.remove(leftNode);list.remove(rightNode);list.add(parent);}return list.get(0);}

返回的为Root结点,非叶子节点的Byte属性都为null,叶子结点的Byte属性不为null

霍夫曼树->霍夫曼编码表,将表存在Map中

    //由霍夫曼树得到霍夫曼编码表static Map<Byte, String> huffmanCodes = new HashMap();//存放编码static StringBuilder stringBuilder = new StringBuilder();//初始为null/*** @param node          传入root结点* @param code          路径:左子结点=0;右子结点=1* @param stringBuilder 用于拼接路径*/private static void getCodes(ByteNode node, String code, StringBuilder stringBuilder) {StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);//每次调用getCodes方法都要new一个StringBuilder,否则回溯时StringBuilder的值并不会回溯stringBuilder1.append(code);if (node != null) {if (node.data == null) {//非叶子结点getCodes(node.left, "0", stringBuilder1);//向左递归getCodes(node.right, "1", stringBuilder1);//向右递归} else//到达叶子结点huffmanCodes.put(node.data, stringBuilder1.toString());}}

输出:Map<Byte, String>

字符串->根据霍夫曼编码进行压缩,存放到byte[]数组

//数据压缩:将一个字符串利用霍夫曼编码压缩后存入byte[]数组public static byte[] zip(String str) {//获得霍夫曼编码表List<ByteNode> list = getList(str);ByteNode root = getHuffManTree(list);getCodes(root, "", new StringBuilder());//将编码表按原字符串的顺序放入StringBuilder中byte[] bytes = str.getBytes();StringBuilder stringBuilder = new StringBuilder();for (byte b : bytes)stringBuilder.append(huffmanCodes.get(b));//再存放在Byte[]数组中,每个元素存8位int len;//返回的byte数组的长度 等价于len=(stringBuilder.length()+7)/8if (stringBuilder.length() % 8 == 0)len = stringBuilder.length() / 8;elselen = stringBuilder.length() / 8 + 1;byte[] by = new byte[len];int index = 0;for (int i = 0; i < stringBuilder.length(); i += 8) {String strByte;if (i + 8 > stringBuilder.length()) {//最后不足8位strByte = stringBuilder.substring(i);//截取从第i位开始,一直到结束的字符串} else {strByte = stringBuilder.substring(i, i + 8);}//8位二进制会被识别为补码,将转换为原码,再转为10进制保存在by[]数组中by[index] = (byte) Integer.parseInt(strByte, 2);index++;}return by;}

8位霍夫曼编码对应存储在byte[ ]数组中:

10101000原码是:11010110,转为10进制为-88

对压缩后的数组解压:

(1)先写一个方法,能将byte转为二进制字符串

//数据解压(1):将存储霍夫曼编码的byte数组中每个值转为原字符串//flag:判断是否是byte数组的最后一个值,因为最后一个值对应的霍夫曼编码可能不足8位private static String byteToBitString(boolean flag, byte b) {int temp = b;if (flag) {temp |= 256;//当b为正数,原码=补码,但结果可能不足8位->将其转为二进制,再与1 0000 0000求或进行位数扩充,取后八位仍是b的原码}String str = Integer.toBinaryString(temp);//b转换为二进制,再转为其补码保存在s里;由于String存储的字节数更大,只需要s的后8位if (flag) {return str.substring(str.length() - 8);//从str.length()-8开始,至字符串结束,共8位}else return str;//若byte数组最后一个值为正,其对应的霍夫曼编码可能8位也可能不足8位,直接返回即可;为负,其对应的霍夫曼编码仍为8位}

(2)对压缩后的byte[ ]数组进行解压

//数据解压(2)//by[] 是原字符串经霍夫曼编码后的数组private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] by){StringBuilder stringBuilder = new StringBuilder();//存放二进制字符串//将byte数组转为二进制的字符串for (int i=0;i<by.length;i++){boolean flag=(i!=by.length-1);//当在by数组最后一个值时,flag=falseString s = byteToBitString(flag, by[i]);stringBuilder.append(s);}//把字符串按照霍夫曼编码进行解码//将原编码表进行反向,方便获取编码对应的字符Map<String,Byte> map=new HashMap();for (Map.Entry<Byte,String> entry:huffmanCodes.entrySet()){map.put(entry.getValue(),entry.getKey());}List<Byte> list=new ArrayList();//截取的字符存放到List中//开始截取for (int i=0;i<stringBuilder.length();){int count=1;boolean flag=true;Byte b=null;//截取字符串,直至与map中的String能够匹配while (flag){String key =stringBuilder.substring(i,i+count);b=map.get(key);if (b==null)//没匹配上count++;else//匹配上了flag=false;}list.add(b);i+=count;}//List中的字符放入byte[]byte[] b=new byte[list.size()];for (int i=0;i<list.size();i++){b[i]=list.get(i);}return b;}

将一个文件进行压缩:

//将一个文件进行压缩public static void zipFile(String srcFile, String dstFile) throws Exception {FileInputStream fis = new FileInputStream(srcFile);byte[] b = new byte[fis.available()];//fis.available()返回文件的大小fis.read(b);//文件的内容写入byte数组中fis.close();byte[] zip = zip(new String(b));FileOutputStream fos = new FileOutputStream(dstFile);ObjectOutputStream oos = new ObjectOutputStream(fos);//利用对象流,写入霍夫曼编码,有利于恢复原文件oos.writeObject(zip);oos.writeObject(huffmanCodes);oos.close();fos.close();}

将一个文件进行解压:

//将文件进行解压public static void decodeFile(String zipFile, String dstFile) throws Exception {FileInputStream fis = new FileInputStream(zipFile);//用对象输入流得到输入的文件ObjectInputStream ois = new ObjectInputStream(fis);byte[] by = (byte[]) ois.readObject();Map<Byte, String> map = (Map<Byte, String>) ois.readObject();//解码byte[] decode = decode(map, by);//将数据写入文件FileOutputStream fos = new FileOutputStream(dstFile);fos.write(decode);fos.close();ois.close();fis.close();}

注意点:

(1)输出字符类型,byte型与int型比较

byte b = 'a';

sout(b)-> 97

byte b1 = 40;

sout(b1) -> 40

int i = 97;

int i1 = 40;

sout(i == b) -> true

sout(i1 == b1) -> true

(2)String与byte关系,以及相互转换

String=byte[8],1个byte字节能存8位无符号数字

byte[ ] b = {'a','b'};

sout(b); -> [b@41628346

sout(Arrays.toString(b)); -> [97,98]

sout(new String(b)); -> ab

String转为byte[ ]数组:

String str ="I like java";

byte[ ] by = str.getBytes();

(3)String拼接使用StringBuilder

StringBuilder线程不安全,StringBuffer线程安全,一般用前者;

拼接方法一:

String s = "hello" 会在常量池开辟一个内存空间来存储”hello"。

s += "world"会先在常量池开辟一个内存空间来存储“world"。然后再开辟一个内存空间来存储”helloworld“。

这么以来,001与002就成为了垃圾内存空间了。这么简单的一个操作就产生了两个垃圾内存空间,如果有大量的字符串拼接,将会造成极大的浪费。

拼接方法二:

StringBuilder的字符串拼接是直接在原来的内存空间操作的,即直接在hello这个内存空间把hello拼接为helloworld。

StringBuilder s1 = new StringBuilder("hello");

s1.append("world");

sout(s1) ->"helloword"

转为String:

s1.toString();

Huffman霍夫曼树,霍夫曼编码相关推荐

  1. 数据结构与算法之Huffman tree(赫夫曼树 / 霍夫曼树 / 哈夫曼树 / 最优二叉树)

    目录 赫夫曼树概述 定义 构造赫夫曼树步骤 代码实现 赫夫曼树概述 HuffmanTree因为翻译不同所以有其他的名字:赫夫曼树.霍夫曼树.哈夫曼树 赫夫曼树又称最优二叉树,是一种带权路径长度最短的二 ...

  2. huffman树_笃学不倦|c语言构造哈夫曼树哈夫曼编码

    艾薇巴蒂!许久不见甚是想念,想必这"涨姿势"的时刻大家已经期待许久了!今天我们要共同学习的是c语言构造哈夫曼树-哈夫曼编码 构造哈夫曼树 首先,我们需要了解哈夫曼树是什么: 相关知 ...

  3. 哈夫曼树 哈夫曼编码

    哈夫曼树 哈夫曼树的定义:设二叉树具有 n 个带权值的叶节点,那么从根节点到各个叶节点的路径长度与相应叶节点权值的乘积的和,叫作二叉树的带权路径长度 WPL (Weighted Path Length ...

  4. 哈夫曼树的构建、编码以及带权路径长计算

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

  5. 15哈夫曼树/哈夫曼编码

    文章目录 哈夫曼树的基本概念 哈夫曼树的特点 哈夫曼树的构造算法 1. 哈夫曼树的构造过程 代码实现 哈夫曼编码 文件的编码和解码 哈夫曼树的基本概念 哈夫曼树又称为最优树,作用是找到一种效率最高的判 ...

  6. c语言最优树的构造,哈夫曼树的构造及编码 Haffman树的构造及其编码

    写出构造完整的哈夫曼树的编码 void HuffmanCoding(HuffmanCode HC[], int w[], int n) // w存放n个字符的权值(均>0),构造哈夫曼树HT, ...

  7. 哈夫曼树(带权路径长度+树的带权路径长度+哈夫曼树定义+构造哈夫曼树+哈夫曼树性质+哈夫曼编码+计算平均码长-这里指WPL)

    带权路径长度 树的带权路径长度WPL 哈夫曼树 哈夫曼树构造 哈夫曼树性质 哈夫曼编码 固定长度编码 可变长编码 前缀编码 固定长度编码.可变长编码.前缀编码.哈夫曼编码 思维倒图 试题

  8. 快速画出哈夫曼树/霍夫曼树/最优树

    2019独角兽企业重金招聘Python工程师标准>>> 哈夫曼树(霍夫曼树)又称为最优二叉树. n个叶子结点的哈夫曼树共有几个结点 ? Huffman 树是所谓的正则二叉树,只有度为 ...

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

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

  10. 最全哈夫曼树哈夫曼编码讲解,兄弟你值得拥有

    目录 1.哈夫曼树的概念         路径概念         路径长度概念         节点的带权路径长度         树的带权路径长度 2.构建哈夫曼树的步骤 3.构建哈夫曼树的完整代 ...

最新文章

  1. 自制启动盘分享(30天倒计时)
  2. Python学习day16-模块基础
  3. kinect内参数的标定
  4. [ZJOI2005]午餐 贪心+dp
  5. 隐藏a标签seo_SEO网站优化,新手SEO常犯的五个错误!
  6. 对C#中的Close()和Dispose()的浅析
  7. Linux 中断详解
  8. 惠普电脑u盘重装系统步骤_HP惠普电脑怎么用U盘装系统
  9. 智慧体检中心管理系统方案/APP/小程序/公众号/网站
  10. Java中分布式概念
  11. MapServer+OpenLayers5+Vue实现栅格图层数据查询
  12. TC27x启动过程(2)-TC277
  13. SAP Transactions
  14. windows中用注册表删除guest账户
  15. Word处理控件Aspose.Words功能演示:使用 Java 将 Word 文档转换为 Markdown
  16. FMG首席执行官被控误导股市
  17. 【元宇宙经济学】元宇宙经济的四要素
  18. SQL 20008 加密函数
  19. linux下设置MySQL密码
  20. python爬虫抓图_Python 爬虫网页抓图保存

热门文章

  1. mysql mha文档_mysql mha
  2. js实现将页面中的数据表格导出为图片,js实现导出excl表格(内含三个插件)
  3. 2022年12月安全事件盘点
  4. python使用selenium爬取boos,100%爬取成功,绕过cookie
  5. CSS 父选择器 :has()
  6. win本机安装magenta教程
  7. Android等待对话框(做一个带动态效果的对话框)
  8. 解决esxi主机vmware 无法清除磁盘的报错
  9. clumsy网络异常测试
  10. 光影魔术手在win7下报错——缺少文件atl71.dll