赫夫曼树

基本介绍:

(1)给定n个权值作为n给叶子节点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称这样的二叉树为最优二叉树,也称哈夫曼树(HuffmanTree),还有的树翻译为霍夫曼树.

(2)赫夫曼树是带权路径长度最短的树,权值较大的节点离根较近.

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

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

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

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

(4)WPL最小的介绍赫夫曼树

赫夫曼树创建思路图解

给你一个数列{13,7,8,3,29,6,1},要求转成一颗赫夫曼树

思路分析

构建赫夫曼树的步骤:

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

(2)取出根节点权值最小的两颗二叉树

(3)组成一颗新的二叉树,该新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和

(4)再将这颗新的二叉树,以根节点的权值大小再次排序,不断重复1-2-3-4的步骤,直到数列中,所有的数据都被处理,就得到一颗赫夫曼树

(5)图解

代码实现

package huffmantree;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/*** 赫夫曼树*/
public class HuffmanTree {public static void main(String[] args) {int arr[] = {13, 7, 8, 3, 29, 6, 1};Node root = createHuffmanTree(arr);//测试preOrder(root);}//编写一个前序遍历方法public static void preOrder(Node root){if (root != null){root.preOrder();}else {System.out.println("该赫夫曼是空树");}}//创建赫夫曼树的方法/**** @param arr  需要创建成赫夫曼树的数组* @return 创建和后的赫夫曼树的root节点*/public static Node createHuffmanTree(int[] arr) {//第一步为了方便操作//1.遍历arr数组//2.将arr的每个元素构成一个Node//3.将Node 放入到ArrayList中List<Node> nodes = new ArrayList<>();for (int value : arr) {nodes.add(new Node(value));}//我们处理的过程是循环的过程while (nodes.size() > 1) {//排序从小到大Collections.sort(nodes);//取出根节点权值最小的两颗 二 叉树//(1)取出权值最小的节点(二叉树)Node leftNode = nodes.get(0);//(2)取出权值第二小的节点(二叉树)Node rightNode = nodes.get(1);//(3)构建一颗新的二叉树Node parent = new Node(leftNode.value + rightNode.value);parent.left = leftNode;parent.right = rightNode;//(4)从ArrayList删除处理过的二叉树nodes.remove(leftNode);nodes.remove(rightNode);//(5)将parent加入到nodesnodes.add(parent);// Collections.sort(nodes);}//返回赫夫曼树的root节点return nodes.get(0);}
}//创建节点类
//为了让Node 对象持续排序Collections集合排序
//让Node 实现Comparable接口
class Node implements Comparable<Node>{int value;//节点权值Node left; //指向左子节点Node right;//指向右字节点//写一个前序遍历public void preOrder(){System.out.println(this);if (this.left != null){this.left.preOrder();}if (this.right != null){this.right.preOrder();}}public Node(int value){this.value = value;}@Overridepublic String toString() {return "Node{" +"value=" + value +'}';}@Overridepublic int compareTo(Node o) {//从小到大排序return this.value - o.value;}
}

赫夫曼编码

基本介绍

(1)赫夫曼编码也翻译为哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,属于一种程序算法

(2)赫夫曼编码是赫夫曼树在电讯通信中的经典应用之一

(3)赫夫曼编码广泛地用于数据文件压缩.其压缩率通常在20%~90%之间

(4)赫夫曼编码是可变字长编码(VLC)的一种.Huffman于1952年提出一种编码方法,称之为最佳编码

原理剖析

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

 传输的字符串
(1)illike like like java do you like a java
(2) d:1y:1 u:1 j:2 v:2 o:2 l:4 k:4 e:4 i:5 a:5 :9|/各个字符对应的个数
(3)按照上面字符出现的次数构建一颗赫夫曼树,次数作为权值
构成赫夫曼树的步骚:

(1)从小到大进行排序,将每一个数据,每个数据都是一个节点,每个节点可以看成是一颗最简单的二叉树2)取出根节点权值最小的两颗二叉树
(3)组成一颗新的二叉树,该新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和
(4)再将这颗新的二叉树,以根节点的权值大小再次排序,不断重复1-2-3-4的步骤,直到数列中,所有的数据都被处理,就得到-颗赫夫曼树

(4)根据赫夫曼树,给各个字符,规定编码(前缀编码),向左的路径为o向右的路径为1,编码如下:o: 1000 u: 10010 d: 100110 y: 100111 i: 101
a : 110k: 1110 e: 1111j: 0000v: 0001l: 001:01
(5)按照上面的赫夫曼编码,我们的"ilike like like java do you like a java”字符串对应的编码为(注意这里我们使用的无损压缩)
1010100110111101111010011011110111101001101111011110100001100001110011001111000011001111000100100100110111101111011100100001100001110通过赫夫曼编码处理长度为133
(6)长度为:133
说明:
原来长度是359,压缩了(359-133)/359=62.9%
此编码满足前缀编码,即字符的编码都不能是其他字符编码的前缀。不会造成匹配的多义性

 注意事项

注意:这个赫夫曼树根据排序方法不同,也可能不太一样,这样对应的赫夫曼编码也不完全一样,但是wpl是一样的,都是最小的,最后生成的赫夫曼编码的长度是一样的,比如:我们让每次生成的新的二叉树总是排在权值相同的二叉树的最后一个,则生成的二叉树为:

数据压缩(创建赫夫曼树)

将给出的一段文本,比如 "ilike like like java do you like a java”,根据前面的讲的赫夫曼编码原理,对其进行数据压缩处理形式
"10101001101111011010011011011101001101111011110100001100001110011001111000011001111000100100100110111101111011100100001100001110
步骤1:根据赫夫曼编码压缩数据的原理,需要创建"i like like like java do youlike a java”对应的赫夫曼树.

创建赫夫曼数核心代码:

 //通过list创建赫夫曼树private static Node createHuffmanTree(List<Node> nodes){while(nodes.size() >1){//排序,从小到大Collections.sort(nodes);//取出第一颗最小的二叉树Node leftNode = nodes.get(0);//取出第二轮最小的二叉树Node rightNode = nodes.get(1);//创建一颗新的二叉树,它的根节点没有date ,只有权值Node parent = new Node(null,leftNode.weight + rightNode.weight);parent.left = leftNode;parent.right = rightNode;//将已经处理的两颗二叉树从nodes删除nodes.remove(leftNode);nodes.remove(rightNode);//将新的二叉树,加入到nodesnodes.add(parent);}//nodes 最后的节点,就是赫夫曼树的根节点return nodes.get(0);}

数据压缩(生成赫夫曼编码和赫夫曼编码后的数据)

最佳实践-数据压缩(生成赫夫曼编码和赫夫曼编码后的数据)
我们已经生成了赫夫曼树,下面我们继续完成任务
(1)生成赫夫曼树对应的赫夫曼编码,如下表:
 =O1 a=100 d=11000 u=11001 e=1110 v=11011 i=101 y=11010 j=0010 k=1111 l=O0(o=0011
(2)使用赫夫曼编码来生成赫夫曼编码数据,即按照上面的赫夫曼编码,将"i like
like like java do you like a java”字符串生成对应的编码数据,形式如下.1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
思路:前面已经分析过了,而且我们讲过了生成赫夫曼编码的具体实现。

核心代码实现:

 /*** 为了方便调用,我们重载getCodes* @param root 根节点* @return huffmanCodes*/private static Map<Byte,String>getCods(Node root){if (root == null){return  null;}//处理root的左子树getCods(root.left,"0",stringBuilder);//处理root右子树getCods(root.right,"1",stringBuilder);return huffmanCodes;}/*** 功能:将传入的node节点的所有叶子节点的赫夫曼编码得到,并放入到huffmanCodes集合中* @param node 传入节点* @param code 路径 :左子节点0,右子节点1* @param stringBuilder 用于拼接路径*/private static void getCods(Node node, String code ,StringBuilder stringBuilder) {StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);//将cods 加入到stringBuffer2stringBuilder2.append(code);if (node != null) { //如果node ==null不处理//判断当前node 是叶子节点还是非叶子节点if (node.date == null) {//非叶子节点//递归处理//向左递归getCods(node.left, "0", stringBuilder2);//向右递归getCods(node.right, "1", stringBuilder2);} else { //说明是一个叶子节点//就表示找到了某个叶子节点的最后huffmanCodes.put(node.date, stringBuilder2.toString());}}}

数据解压(使用赫夫曼编码解压)

使用赫夫曼编码来解码数据,具体要求是

(1)前面我们得到了赫夫曼编码和对应的编码
byte[],即:[-88,-6念,-56,-65,-56,-65,-55,77,-57,6,-24,-14,-117,-4,-60,-90,28]
(2)现在要求使用赫夫曼编码,进行解码,又
重新得到原来的字符串"i like like like
java do you like a java"
思路:解码过程,就是编码的一个逆向操作。

解压核心代码:

/*** 将一个byte 转成一个二进制的字符串* @param b 传入的byte* @param flag 标识是否需要补高位如果是true,表示需要补高位,如果是false表示不补* @return 是该b 对应的二进制的字符串(注意是按补码返回)*/private static String byteToBitString(boolean flag, byte b){//使用变量保存bint temp = b;//将b转成int//如果是正数我们还存在补高位问题if (flag) {temp |= 256; //按位与256 1 0000 0000 0000 0001 =>1 0000 0001}String str = Integer.toBinaryString(temp); //返回的是temp对应的二进制的补码if (flag){return str.substring(str.length() - 8);}else{return str;}}/***编写一个方法,完成对压缩数据的解码* @param huffmanCodes 赫夫曼编码表map* @param huffmanBytes 赫夫曼编码得到字节数组* @return 就是原来字符串对应的数组*  如何将数据进行解压(解码)*         思路*         1.将huffmanCodeBytes[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]*         重写先转成赫夫曼编码对应的二进制的字符串"10101000010111...."*         2.赫夫曼编码对应的二进制的字符串"1010100010111..=>对照赫夫曼编码=>i like like like java do you like a java"*/private static byte[] decode(Map<Byte,String> huffmanCodes, byte[] huffmanBytes){//1.先得到huffmanBytes 对应的二进制的字符串,StringBuilder stringBuilder = new StringBuilder();//将byte数组转成二进制的字符串for (int i = 0 ; i < huffmanBytes.length; i++){byte b = huffmanBytes[i];//判断是不是最后一个字节boolean flag = (i == huffmanBytes.length - 1);stringBuilder.append(byteToBitString(!flag,b));}//把字符串按照指定的赫夫曼编码进行解码//把赫夫曼编码表进行调换,因为要反向查询 97->100 100->1Map<String,Byte> map = new HashMap<>();for (Map.Entry<Byte,String> entry: huffmanCodes.entrySet()){map.put(entry.getValue(),entry.getKey());}//创建一个集合,存放byteList<Byte> list = new ArrayList<>();//i 可以理解成就是索引,扫描stringBuilderfor (int i = 0 ;i < stringBuilder.length();){int count = 1;//小的计数器boolean flag = true;Byte b = null;while (flag){//取出一个'1' '0' 递增的取出key 1String key = stringBuilder.substring(i,i+count);//i不动,让count移动,匹配到指定一个字符b = map.get(key);if (b == null){//说明没有匹配到count++;}else {//匹配到flag = false;}}list.add(b);i +=count;//i直接移动到count}//当for循环结束后,我们list中就存放了所有的字符"i like like like java do you like a java"//把list中的数据放入到byte[]并返回byte b[] = new byte[list.size()];for (int i = 0; i < b.length; i++){b[i] = list.get(i);}return b;}

文件压缩

我们学习了通过赫夫曼编码对-个字符串进行编码和解码,下面我们来完成对文件的压缩和解压,具体要求:
给你一个图片文件,要求对其进行无损压缩,看看压缩效果如何。
1)思路:读取文件>得到赫夫曼编码表>完成压缩
2)代码实现:

代码实现

 /*** 编写一个方法,将一个文件进行压缩* @param srcFile  你传入希望压缩的文件的全路径* @param dstFile 我们压缩后将文件放到那个目录*/public static void zipFile(String srcFile,String dstFile){//创建输出流OutputStream os = null;ObjectOutputStream oos = null;//创建文件输入流FileInputStream is =  null;try {//创建一个和源文件大小一样的byte[]is = new FileInputStream(srcFile);byte[] b = new byte[is.available()];//读取文件is.read(b);//直接对源文件进行压缩byte[] huffmanBytes = huffmanZip(b);//创建文件的输出流,存放压缩文件os = new FileOutputStream(dstFile);//创建一个和文件输出流关联的ObjectOutputStreamoos = new ObjectOutputStream(os);//把赫夫曼编码后的字节数组写入压缩文件oos.writeObject(huffmanBytes);//这里我们以对象流的方式写入赫夫曼编码,是为了我们以后恢复源文件时使用//注意,一定要把赫夫曼编码写入压缩文件oos.writeObject(huffmanCodes);}catch (Exception e){System.out.println(e.getMessage());}finally {try {is.close();oos.close();os.close();}catch (Exception e){System.out.println(e.getMessage());}}}

文件解压(文件恢复)

具体要求:将前面压缩的文件,重新恢复成原来的文件

思路:读取压缩文件(数据和和赫夫曼编码表)->完成解压

代码实现:

 /*** 编写一个方法,完成对压缩文件进行解压* @param zipFile 准备解压的文件* @param dstFile  将文件解压到那个路径*/public static void unZipFile(String zipFile ,String dstFile){//定义文件输入流InputStream is = null;//定义对象输入流ObjectInputStream ois = null;//定义文件输出流OutputStream os = null;try{//创建文件输入流is = new FileInputStream(zipFile);//创建一个和is关联的对象输入流ois = new ObjectInputStream(is);//读取byte数组 huffmanBytebyte [] huffmanBytes = (byte[])ois.readObject();//读取赫夫曼编码表Map<Byte,String> huffmanCodes = (Map<Byte, String>)ois.readObject();//解码byte[] bytes = decode(huffmanCodes,huffmanBytes);//将bytes写入目标文件os = new FileOutputStream(dstFile);//写数据到dstFile 文件os.write(bytes);}catch (Exception e){System.out.println(e.getMessage());}finally {try {os.close();ois.close();is.close();}catch (Exception e2){System.out.println(e2.getMessage());}}}

赫夫曼编码压缩文件注意事项

(1)如果文件本身就是经过压缩处理的,那么使用赫夫曼编码再压缩效率不会有明显变化,比如视频,ppt等文件

(2)赫夫曼编码是按字节来处理的,因此可以处理所有文件(二进制,文本文件)

(3)如果一个文件中的内容,重复数据不多,压缩效果也不会很明显

代码汇总

前面所有的代码

package huffmanCode;import java.io.*;
import java.util.*;/*** 赫夫曼编码  压缩和解码*/
public class HuffmanCode {public static void main(String[] args) {/*//测试压缩文件String srcFile = "D:\\新建 文本文档.txt";String dstFile ="D:\\dst2.zip" ;zipFile(srcFile,dstFile);System.out.println("压缩文件ok~~~");*///测试解压文件String zipFile ="D:\\dst2.zip" ;String dstFile = "D:\\111111111112.jpg";unZipFile(zipFile,dstFile);System.out.println("解压成功");/*String content = "i like like like java do you like a java";byte[] contentBytes = content.getBytes();System.out.println(contentBytes.length); //40byte[] huffmanCodesBytes = huffmanZip(contentBytes);System.out.println("压缩后的结果=" + Arrays.toString(huffmanCodesBytes) + "长度=" + huffmanCodesBytes.length);//测试byteToBitString方法//System.out.println(byteToBitString((byte)1));byte[] sourceBytes = decode(huffmanCodes,huffmanCodesBytes);System.out.println("原来的字符串="+ new String(sourceBytes));*///分步过程/*List<Node> nodes = getNodes(contentBytes);System.out.println(nodes);//测试一次,创建的二叉树System.out.println("赫夫曼树");Node huffmanTreeRoot = createHuffmanTree(nodes);System.out.println("前序遍历");huffmanTreeRoot.preOrder();//测试是否生成了对应的赫夫曼编码Map<Byte,String> huffmanCodes = getCods(huffmanTreeRoot);System.out.println("~生成的赫夫曼编码表"+huffmanCodes);//17//测试byte[] huffmanCodeBytes = zip(contentBytes,huffmanCodes);System.out.println("huffmanCodeBytes="+Arrays.toString(huffmanCodeBytes));//发生huffmanCodeBytes 数组*/}/*** 编写一个方法,将一个文件进行压缩* @param srcFile  你传入希望压缩的文件的全路径* @param dstFile 我们压缩后将文件放到那个目录*/public static void zipFile(String srcFile,String dstFile){//创建输出流OutputStream os = null;ObjectOutputStream oos = null;//创建文件输入流FileInputStream is =  null;try {//创建一个和源文件大小一样的byte[]is = new FileInputStream(srcFile);byte[] b = new byte[is.available()];//读取文件is.read(b);//直接对源文件进行压缩byte[] huffmanBytes = huffmanZip(b);//创建文件的输出流,存放压缩文件os = new FileOutputStream(dstFile);//创建一个和文件输出流关联的ObjectOutputStreamoos = new ObjectOutputStream(os);//把赫夫曼编码后的字节数组写入压缩文件oos.writeObject(huffmanBytes);//这里我们以对象流的方式写入赫夫曼编码,是为了我们以后恢复源文件时使用//注意,一定要把赫夫曼编码写入压缩文件oos.writeObject(huffmanCodes);}catch (Exception e){System.out.println(e.getMessage());}finally {try {is.close();oos.close();os.close();}catch (Exception e){System.out.println(e.getMessage());}}}/*** 编写一个方法,完成对压缩文件进行解压* @param zipFile 准备解压的文件* @param dstFile  将文件解压到那个路径*/public static void unZipFile(String zipFile ,String dstFile){//定义文件输入流InputStream is = null;//定义对象输入流ObjectInputStream ois = null;//定义文件输出流OutputStream os = null;try{//创建文件输入流is = new FileInputStream(zipFile);//创建一个和is关联的对象输入流ois = new ObjectInputStream(is);//读取byte数组 huffmanBytebyte [] huffmanBytes = (byte[])ois.readObject();//读取赫夫曼编码表Map<Byte,String> huffmanCodes = (Map<Byte, String>)ois.readObject();//解码byte[] bytes = decode(huffmanCodes,huffmanBytes);//将bytes写入目标文件os = new FileOutputStream(dstFile);//写数据到dstFile 文件os.write(bytes);}catch (Exception e){System.out.println(e.getMessage());}finally {try {os.close();ois.close();is.close();}catch (Exception e2){System.out.println(e2.getMessage());}}}/***编写一个方法,完成对压缩数据的解码* @param huffmanCodes 赫夫曼编码表map* @param huffmanBytes 赫夫曼编码得到字节数组* @return 就是原来字符串对应的数组*  如何将数据进行解压(解码)*         思路*         1.将huffmanCodeBytes[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]*         重写先转成赫夫曼编码对应的二进制的字符串"10101000010111...."*         2.赫夫曼编码对应的二进制的字符串"1010100010111..=>对照赫夫曼编码=>i like like like java do you like a java"*/private static byte[] decode(Map<Byte,String> huffmanCodes, byte[] huffmanBytes){//1.先得到huffmanBytes 对应的二进制的字符串,StringBuilder stringBuilder = new StringBuilder();//将byte数组转成二进制的字符串for (int i = 0 ; i < huffmanBytes.length; i++){byte b = huffmanBytes[i];//判断是不是最后一个字节boolean flag = (i == huffmanBytes.length - 1);stringBuilder.append(byteToBitString(!flag,b));}//把字符串按照指定的赫夫曼编码进行解码//把赫夫曼编码表进行调换,因为要反向查询 97->100 100->1Map<String,Byte> map = new HashMap<>();for (Map.Entry<Byte,String> entry: huffmanCodes.entrySet()){map.put(entry.getValue(),entry.getKey());}//创建一个集合,存放byteList<Byte> list = new ArrayList<>();//i 可以理解成就是索引,扫描stringBuilderfor (int i = 0 ;i < stringBuilder.length();){int count = 1;//小的计数器boolean flag = true;Byte b = null;while (flag){//取出一个'1' '0' 递增的取出key 1String key = stringBuilder.substring(i,i+count);//i不动,让count移动,匹配到指定一个字符b = map.get(key);if (b == null){//说明没有匹配到count++;}else {//匹配到flag = false;}}list.add(b);i +=count;//i直接移动到count}//当for循环结束后,我们list中就存放了所有的字符"i like like like java do you like a java"//把list中的数据放入到byte[]并返回byte b[] = new byte[list.size()];for (int i = 0; i < b.length; i++){b[i] = list.get(i);}return b;}/*** 将一个byte 转成一个二进制的字符串* @param b 传入的byte* @param flag 标识是否需要补高位如果是true,表示需要补高位,如果是false表示不补* @return 是该b 对应的二进制的字符串(注意是按补码返回)*/private static String byteToBitString(boolean flag, byte b){//使用变量保存bint temp = b;//将b转成int//如果是正数我们还存在补高位问题if (flag) {temp |= 256; //按位与256 1 0000 0000 0000 0001 =>1 0000 0001}String str = Integer.toBinaryString(temp); //返回的是temp对应的二进制的补码if (flag){return str.substring(str.length() - 8);}else{return str;}}/***使用给一个方法,将前面的方法封装起来,便于我们调用* @param bytes 原始的字符串对应的字节数组* @return 是经过 赫夫曼编码压缩后的字节数组*/private static byte[] huffmanZip(byte[] bytes){List<Node> nodes = getNodes(bytes);//根据nodes创建赫夫曼树Node huffmanTreeRoot = createHuffmanTree(nodes);//生成对应的赫夫曼编码Map<Byte,String> huffmanCodes = getCods(huffmanTreeRoot);//根据生产的赫夫曼编码压缩,得到压缩后的赫夫曼编码字节数组byte[] huffmanCodeBytes = zip(bytes,huffmanCodes);return huffmanCodeBytes;}/***编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]* @param bytes 中时原始的字符串对应的byte[]* @param huffmanCodes 生产的赫夫曼编码map* @return 返回赫夫曼编码处理后的byte[]**/private static byte[] zip(byte[] bytes, Map<Byte,String> huffmanCodes){//1.利用huffmanCodes 将bytes 转成赫夫曼编码的对应字符串StringBuilder stringBuilder = new StringBuilder();//遍历bytes 数组for (byte b : bytes){stringBuilder.append(huffmanCodes.get(b));}//将 对应的字符串,转成byte[]//统计返回的byte[]huffmanCodeBytes 长度//一句话 int len =(stingBuilder.length()+7)/8;int len;if (stringBuilder.length() % 8 ==0){len =stringBuilder.length() / 8;}else {len = stringBuilder.length() / 8 + 1;}//创建存储压缩后的byte数组byte [] huffmanCodeBytes = new byte[len];int index = 0; //记录是第一个bytefor (int i = 0; i < stringBuilder.length(); i += 8){//因为是每8位对应一个byte,所有步长是+8String strByte;if (i + 8 > stringBuilder.length()){ //不够八位strByte = stringBuilder.substring(i);}else {strByte = stringBuilder.substring(i, i + 8);}//将strByte 转成一个byte,放入huffmanCodeByteshuffmanCodeBytes[index] = (byte)Integer.parseInt(strByte,2);index++;}return huffmanCodeBytes;}/*** 生成赫夫曼树对应的编码表*     思路分析:*     1.将赫夫曼编码表存放在Map<Byte,String>形式*     2.在生成赫夫曼编码表示,需要去拼接路径,定义一个StingBuilder 储存某个叶子节点的路径*/static Map<Byte,String> huffmanCodes = new HashMap<>();static StringBuilder stringBuilder =new StringBuilder();/*** 为了方便调用,我们重载getCodes* @param root 根节点* @return huffmanCodes*/private static Map<Byte,String>getCods(Node root){if (root == null){return  null;}//处理root的左子树getCods(root.left,"0",stringBuilder);//处理root右子树getCods(root.right,"1",stringBuilder);return huffmanCodes;}/*** 功能:将传入的node节点的所有叶子节点的赫夫曼编码得到,并放入到huffmanCodes集合中* @param node 传入节点* @param code 路径 :左子节点0,右子节点1* @param stringBuilder 用于拼接路径*/private static void getCods(Node node, String code ,StringBuilder stringBuilder) {StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);//将cods 加入到stringBuffer2stringBuilder2.append(code);if (node != null) { //如果node ==null不处理//判断当前node 是叶子节点还是非叶子节点if (node.date == null) {//非叶子节点//递归处理//向左递归getCods(node.left, "0", stringBuilder2);//向右递归getCods(node.right, "1", stringBuilder2);} else { //说明是一个叶子节点//就表示找到了某个叶子节点的最后huffmanCodes.put(node.date, stringBuilder2.toString());}}}//前序遍历的方法private static void preOrder(Node root){if (root != null){root.preOrder();}else {System.out.println("该赫夫曼是空树");}}/*** 将字节数组存放到list中* @param bytes 接受字节数组* @return 返回的就是List 形式 [Node[date=97] ,weight = 5]*/private static List<Node> getNodes(byte[] bytes) {//创建一个ArrayListArrayList<Node> nodes = new ArrayList<>();//遍历bytes ,统计每一个byte出现的次数->map[key ,value]Map<Byte, Integer> counts = new HashMap<>();for (byte b : bytes) {Integer count = counts.get(b);if (count == null) { //Map还没有这个字符数据,第一次counts.put(b, 1);} else {counts.put(b, count + 1);}}//把每个键值对转成一个Node 对象,并加入到nodes集合//遍历mapfor (Map.Entry<Byte,Integer> entry : counts.entrySet()){nodes.add(new Node(entry.getKey(),entry.getValue()));}return nodes;}//通过list创建赫夫曼树private static Node createHuffmanTree(List<Node> nodes){while(nodes.size() >1){//排序,从小到大Collections.sort(nodes);//取出第一颗最小的二叉树Node leftNode = nodes.get(0);//取出第二轮最小的二叉树Node rightNode = nodes.get(1);//创建一颗新的二叉树,它的根节点没有date ,只有权值Node parent = new Node(null,leftNode.weight + rightNode.weight);parent.left = leftNode;parent.right = rightNode;//将已经处理的两颗二叉树从nodes删除nodes.remove(leftNode);nodes.remove(rightNode);//将新的二叉树,加入到nodesnodes.add(parent);}//nodes 最后的节点,就是赫夫曼树的根节点return nodes.get(0);}
}//创建Node ,待数据和权值
class Node implements Comparable<Node>{Byte date; //存放数据int weight;//权值,表示字符出现的次数Node left;Node right;public Node(Byte date, int weight) {this.date = date;this.weight = weight;}@Overridepublic int compareTo(Node o) {//从小到大排序return this.weight -o.weight;}@Overridepublic String toString() {return "Node{" +"date=" + date +", weight=" + weight +'}';}//前序遍历public void preOrder(){System.out.println(this);if (this.left != null){this.left.preOrder();}if (this.right != null){this.right.preOrder();}}
}

数据结构与算法(赫夫曼树,赫夫曼编码)相关推荐

  1. 数据结构与算法——赫夫曼树基本实现

    目录 一.赫夫曼树 1.1 基本介绍 1.2 赫夫曼树创建步骤图解 1.3  代码实现 二.赫夫曼编码 2.1 基本介绍 2.1.1  通讯领域 - 定长编码 - 举例说明 2.1.2  通讯领域 - ...

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

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

  3. 赫夫曼树+赫夫曼编码小结

    赫夫曼树 赫夫曼树定义与原理 从树中一个节点到另一个节点之间的分支构成两个结点之间的路径,路径上的分支数目称做路径长度. 图6-12-4的二叉树a中,根结点到结点D的路径长度就为4,二叉树b中根结点到 ...

  4. 树:赫夫曼树赫夫曼编码

    1,赫夫曼树 1.1,赫夫曼树基本介绍及相关概念 给定n个权值作为n个叶子节点,构造一颗二叉树,若该树的**带权路径长度(WPL)**达到最小,称这样的的二叉树为最优二叉树,也称为赫夫曼树,或者哈夫曼 ...

  5. 数据结构与算法 / 霍夫曼树、霍夫曼编码和解码

    一. 诞生原因 找出存放一串字符所需的最少的二进制编码. 二. 构造方法 首先统计出每种字符出现的频率,即:概率.权值. 例如:频率表 A:60,    B:45,   C:13   D:69   E ...

  6. 数据结构与算法--哈夫曼树及其应用

    一.哈夫曼树的基本概念 1) 路径: 从树中一个结点到另一个结点之间的分支构成这两个结点间的路径 2) 结点的路径长度: 两结点间路径上的分支数           3) 树的路径长度:从树根到每一个 ...

  7. 数据结构与算法--哈夫曼树应用

    第1关:统计报文中各个字符出现的次数 任务描述 本关任务: 给定一串文本,统计其中各个字符出现的次数: 测试说明 平台会对你编写的代码进行测试: 测试输入:` abcdeabcdeabcdabcdab ...

  8. 数据结构Java06【赫夫曼树、概述、原理分析、代码实现(数据压缩、创建编码表、解码、压缩文件、解压文件)】

    学习地址:[数据结构与算法基础-java版]                  

  9. 【数据结构】认识赫夫曼树与赫夫曼编码 上手实现压缩文件和解压

最新文章

  1. 动态创建表格给同一个标签创建点击事件并让点击事件操作内容不一样
  2. 设备 esp32_ESP32-S2 SoC、模组、开发板全面上市
  3. C语言---二进制和文本文件的备份
  4. mysql修改数据存放位置_Mysql 修改数据库存放位置
  5. Step one : 熟悉Unix/Linux Shell 常见命令行 (四)
  6. koa搭建node服务
  7. Linux之vim详解
  8. 云课堂让职业院校软件开发教学更简单
  9. 常见面试题汇总 —— C语言
  10. Labview的CAN通讯
  11. TRAS为springcloud提供高性能的RPC能力
  12. 阿里云迁移工具推荐最佳实践:物理服务器迁移到阿里云
  13. 苹果手机开不了机怎么办
  14. 如何在没有 USB 数据线的情况下使用 Android Studio 在手机中安装 Android
  15. ubuntu 屏幕旋转与重力感应
  16. 公安销售许可证的申请流程-从检测到拿证
  17. 【视频教程】帝国CMS制作网站系列教程14—标签模板及标签讲解
  18. 不均匀光照文本图像的二值化
  19. C语言经典例题100道(求素数最全的方法在这里!!!)
  20. Node版本管理器nvm

热门文章

  1. Linux重定向管道——双通、三通管道一篇学会【CenOs】
  2. 曾经用过的“转正申请书”,给用得着的朋友参考
  3. 记录一下华为面试,已挂
  4. u盘linux下使用,如何在Linux下使用U盘
  5. matlab流程图程序,matlab程序流程图
  6. javascript – 从页面停用浏览器打印选项(页眉,页脚,页边距)?
  7. Android与IOS测试区别
  8. python和django的目录遍历漏洞(任意文件读取)
  9. stm32单片机c语言入门,STM32入门教程1,初学者入门,从零开始,使用keil建立一个简单的ST...
  10. 210. 课程表 II kahn求拓扑图算法