算法 - 赫夫曼编码(对字符串进行压缩 与 解压(解码)) - (对文件进行压缩解压)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.压缩:使用赫夫曼编码进行压缩

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题目

在这里插入图片描述

构建赫夫曼树

package tree.huffmantree;import java.util.*;public class HuffmanCode {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);List<Node1> nodes = getNodes(contentBytes);//System.out.println(nodes);//测试创建二叉树Node1 huffmanTree = createHuffmanTree(nodes);//前序遍历preOrder(huffmanTree);}//前序遍历public static void preOrder(Node1 root){if (root != null){root.preOrder();}else {System.out.println("赫夫曼树为空");}}private  static List<Node1> getNodes(byte [] bytes){//1.创建ArrayListArrayList<Node1> node1s = new ArrayList<>();//遍历bytes 统计乜咯 byte出现的次数,存储每个byte出现的次数 -> mapMap<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()){node1s.add(new Node1(entry.getKey(),entry.getValue()));}return node1s;}//通过List创建赫夫曼树private static Node1 createHuffmanTree(List<Node1> nodes){while (nodes.size() > 1){//排序  从小到大Collections.sort(nodes);//取出第一颗、第二颗最小的二叉树Node1 leftNode = nodes.get(0);Node1 rightNode = nodes.get(1);//创建新的二叉树,新的二叉树没有数据,只有权值Node1 parent = new Node1(null,leftNode.weight + rightNode.weight);parent.left  = leftNode;parent.right = rightNode;//将0,1移除Listnodes.remove(leftNode);nodes.remove(rightNode);//parent加入Listnodes.add(parent);}//nodes最后剩余就是哈弗曼树的根节点return nodes.get(0);}
}
class Node1 implements Comparable<Node1>{Byte data; //存放数据 按照asciiint weight; //权值,表示字符出现的次数Node1 left;Node1 right;//前序遍历public void preOrder(){System.out.println(this);if (this.left != null){this.left.preOrder();}if (this.right != null){this.right.preOrder();}}@Overridepublic int compareTo(Node1 o) {return this.weight - o.weight;}public Node1(Byte data, int weight) {this.data = data;this.weight = weight;}@Overridepublic String toString() {return "Node1{" +"data=" + data +", weight=" + weight +'}';}
}

在这里插入图片描述

//生成赫夫曼树对应的赫夫曼编码//思路://1.将赫夫曼编码表存放在Map<Byte,String> 形式//32->01  97->100 100->11000等等static Map<Byte,String> huffmanCodes = new HashMap<>();//2.在生成赫夫曼编码表时需要拼接路径,创建Stringbuilder存储某个叶子节点的路径static  StringBuilder stringBuilder = new StringBuilder();/*** 功能:将传入的node节点的所有叶子节点赫夫曼编码得到,并放入到赫夫曼集合中* @param node  传入节点* @param code  路径:左子节点是0,右子节点是1* @param stringBuilder 是用于拼接路径*/private static void getCondes(Node1 node,String code, StringBuilder stringBuilder){StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);//将传入的code加入到Stringbuilder2stringBuilder2.append(code);if (node != null){//判断当前node是叶子节点还是非叶子节点if (node.data == null){ //说明是非叶子节点//递归处理//向左递归getCondes(node.left,"0",stringBuilder2);//向右递归getCondes(node.right,"1",stringBuilder2);}else {//说明是叶子节点//就表示找到了某个叶子节点最后huffmanCodes.put(node.data,stringBuilder2.toString());}}}

在这里插入图片描述

//编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回赫夫曼编码压缩后的byte[]/**** @param bytes 原始的字符对应的byte[]* @param huffmanCodes  生成的赫夫曼编码表map* @return  返回赫夫曼编码处理后的byte[]* 举例:String content = "i like like like java do you like a java";* 返回的是字符串"10101000"。。。等等* =>对应byte[] huffmancodeBytes,即8位对应一个byte,放入到huffmanCodeBytes* huffmancodeBytes[0] = 10101000(补码) => byte [推导 10101000 => 10101000 -1 => 10100111(反码) => 11011000(原码)]* huffmancodeBytes[1] = -88*/private static byte[] zip(byte [] bytes, Map<Byte,String> huffmanCodes){//1.利用赫夫曼编码表将传进来的byte数组转成赫夫曼编码字符串StringBuilder stringBuilder = new StringBuilder();//遍历bytes数组for (byte b : bytes){stringBuilder.append(huffmanCodes.get(b));}//按照这个字符串发送肯定是变大了,不行,那么就要将字符串转成byte数组System.out.println(stringBuilder.toString());//统计返回的byte[] huffmanCodeBytes 长度//一句话搞定int len = (stringBuilder.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位对应一个byteString strByte;if (i+8 <= stringBuilder.length()){strByte = stringBuilder.substring(i,i+8);}else {strByte = stringBuilder.substring(i); //-88}//将StringByte转成byte数组放入到huffmanCodeByteshuffmanCodeBytes[index] = (byte) Integer.parseInt(strByte,2);index++;}return huffmanCodeBytes;}

在这里插入图片描述

完整代码

package tree.huffmantree;import java.util.*;public class HuffmanCode {public static void main(String[] args) {String content = "i like like like java do you like a java";byte [] contentBytes = content.getBytes();byte[] bytes = huffmanZip(contentBytes);System.out.println("压缩后的结果: " + Arrays.toString(bytes));//        System.out.println(contentBytes.length);
//
//        List<Node1> nodes = getNodes(contentBytes);
//        //System.out.println(nodes);
//
//        //测试创建二叉树
//        Node1 huffmanTree = createHuffmanTree(nodes);
//        //前序遍历
//        preOrder(huffmanTree);
//
//        //测试是否生成了对应的哈夫曼编码
//        Map<Byte, String> huffmancondes = getCondes(huffmanTree);
//        System.out.println("生成的赫夫曼编码表" + huffmancondes);
//
//        //测试
//        byte[] huffmanCodeBytes = zip(contentBytes, huffmancondes);
//        System.out.println("huffmanCodeBytes=" + Arrays.toString(huffmanCodeBytes));}//封装前面所写的,便于调用/**** @param bytes 原始字符串对应的字节数组* @return  返回的是经过赫夫曼编码处理后的字节数组(压缩后的数组)*/private static byte[] huffmanZip(byte [] bytes){//第一步:创建节点List<Node1> nodes = getNodes(bytes);//第二步:创建赫夫曼树Node1 huffmanTree = createHuffmanTree(nodes);//第三步:生成对应的赫夫曼编码(根据赫夫曼树)Map<Byte, String> hufumanCodes = getCondes(huffmanTree);//第四步:根据赫夫曼编码压缩,生成赫夫曼字节数组byte[] huffmanBytes = zip(bytes, hufumanCodes);return huffmanBytes;}//编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回赫夫曼编码压缩后的byte[]/**** @param bytes 原始的字符对应的byte[]* @param huffmanCodes  生成的赫夫曼编码表map* @return  返回赫夫曼编码处理后的byte[]* 举例:String content = "i like like like java do you like a java";* 返回的是字符串"10101000"。。。等等* =>对应byte[] huffmancodeBytes,即8位对应一个byte,放入到huffmanCodeBytes* huffmancodeBytes[0] = 10101000(补码) => byte [推导 10101000 => 10101000 -1 => 10100111(反码) => 11011000(原码)]* huffmancodeBytes[1] = -88*/private static byte[] zip(byte [] bytes, Map<Byte,String> huffmanCodes){//1.利用赫夫曼编码表将传进来的byte数组转成赫夫曼编码字符串StringBuilder stringBuilder = new StringBuilder();//遍历bytes数组for (byte b : bytes){stringBuilder.append(huffmanCodes.get(b));}//按照这个字符串发送肯定是变大了,不行,那么就要将字符串转成byte数组System.out.println(stringBuilder.toString());//统计返回的byte[] huffmanCodeBytes 长度//一句话搞定int len = (stringBuilder.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位对应一个byteString strByte;if (i+8 <= stringBuilder.length()){strByte = stringBuilder.substring(i,i+8);}else {strByte = stringBuilder.substring(i); //-88}//将StringByte转成byte数组放入到huffmanCodeByteshuffmanCodeBytes[index] = (byte) Integer.parseInt(strByte,2);index++;}return huffmanCodeBytes;}//生成赫夫曼树对应的赫夫曼编码//思路://1.将赫夫曼编码表存放在Map<Byte,String> 形式//32->01  97->100 100->11000等等static Map<Byte,String> huffmanCodes = new HashMap<>();//2.在生成赫夫曼编码表时需要拼接路径,创建Stringbuilder存储某个叶子节点的路径static  StringBuilder stringBuilder = new StringBuilder();//为了调用方便重载getCondesprivate static Map<Byte,String> getCondes(Node1 root){if (root == null){return null;}//处理rootgetCondes(root,"",stringBuilder);return huffmanCodes;}/*** 功能:将传入的node节点的所有叶子节点赫夫曼编码得到,并放入到赫夫曼集合中* @param node  传入节点* @param code  路径:左子节点是0,右子节点是1* @param stringBuilder 是用于拼接路径*/private static void getCondes(Node1 node,String code, StringBuilder stringBuilder){StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);//将传入的code加入到Stringbuilder2stringBuilder2.append(code);if (node != null){//判断当前node是叶子节点还是非叶子节点if (node.data == null){ //说明是非叶子节点//递归处理//向左递归getCondes(node.left,"0",stringBuilder2);//向右递归getCondes(node.right,"1",stringBuilder2);}else {//说明是叶子节点//就表示找到了某个叶子节点最后huffmanCodes.put(node.data,stringBuilder2.toString());}}}//前序遍历public static void preOrder(Node1 root){if (root != null){root.preOrder();}else {System.out.println("赫夫曼树为空");}}private  static List<Node1> getNodes(byte [] bytes){//1.创建ArrayListArrayList<Node1> node1s = new ArrayList<>();//遍历bytes 统计乜咯 byte出现的次数,存储每个byte出现的次数 -> mapMap<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()){node1s.add(new Node1(entry.getKey(),entry.getValue()));}return node1s;}//通过List创建赫夫曼树private static Node1 createHuffmanTree(List<Node1> nodes){while (nodes.size() > 1){//排序  从小到大Collections.sort(nodes);//取出第一颗、第二颗最小的二叉树Node1 leftNode = nodes.get(0);Node1 rightNode = nodes.get(1);//创建新的二叉树,新的二叉树没有数据,只有权值Node1 parent = new Node1(null,leftNode.weight + rightNode.weight);parent.left  = leftNode;parent.right = rightNode;//将0,1移除Listnodes.remove(leftNode);nodes.remove(rightNode);//parent加入Listnodes.add(parent);}//nodes最后剩余就是哈弗曼树的根节点return nodes.get(0);}
}
class Node1 implements Comparable<Node1>{Byte data; //存放数据 按照asciiint weight; //权值,表示字符出现的次数Node1 left;Node1 right;//前序遍历public void preOrder(){System.out.println(this);if (this.left != null){this.left.preOrder();}if (this.right != null){this.right.preOrder();}}@Overridepublic int compareTo(Node1 o) {return this.weight - o.weight;}public Node1(Byte data, int weight) {this.data = data;this.weight = weight;}@Overridepublic String toString() {return "Node1{" +"data=" + data +", weight=" + weight +'}';}
}

2.解压(解码)

  //完成数据解压//思路//1.将huffmanCodeBytes[-88,-65..]重写先转成赫夫曼编码对应的二进制字符串//2.赫夫曼编码对应的二进制字符串根据赫夫曼编码转成字符//编写一个方法,对压缩数据解码/**** @param huffmanCodes 赫夫曼编码表 map* @param huffmanBytes 需要解码的字节数组* @return 返回原来字符串对应的数组*/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++) {//判断是否是最后一个字节boolean flag = (i == huffmanBytes.length - 1);stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));}System.out.println("赫夫曼 解码后 对应的二进制字符串:" + stringBuilder.toString());//把字符串按照指定的赫夫曼编码进行解码//把赫夫曼编码表的k,v进行调换;因为要反向查询Map<String,Byte> map = new HashMap<>();for (Map.Entry<Byte,String> entry : huffmanCodes.entrySet()){map.put(entry.getValue(),entry.getKey());}//System.out.println(map);//创建一个集合存放byteList<Byte> list = new ArrayList<>();for (int i = 0; i < stringBuilder.length();) {int count = 1; //小的计数器boolean flag = true;Byte b = null;while (flag){//1010100010111。。。。String 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中存放了所以的字符//把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需要补高位,如果是最后一个字节不需要补高位* @return*/private static String byteToBitString(boolean flag, byte b){//使用变量保存bint temp = b;//将b转成intif (flag){temp |= 256; //按位与256 1 0000 0000 | 0000 0001 =》1 0000 0001}String str = Integer.toBinaryString(temp);if (flag){return str.substring(str.length() - 8);}else {return str;}}

在这里插入图片描述

3.对文件进行压缩(加入io,通过对象流把赫夫曼编码传入,解压的时候需要用)

//编写一个方法,进行文件压缩public static void zipFile(String srcFile, String dstFile){//创建输出流FileInputStream is = null;//文件输入流OutputStream os = null;ObjectOutputStream oos = null;try{is = new FileInputStream(srcFile);//创建一个和源文件大小一样的btyte[]byte[] b = new byte[is.available()];//读取文件is.read(b);//直接对源文件压缩byte[] huffmanBytes = huffmanZip(b);//创建文件的输出流,存放压缩文件os = new FileOutputStream(dstFile);//创建一个和文件输出流关联的ObjectOutPutStream对象流oos = new ObjectOutputStream(os);//把赫夫曼编码后的字节数组写入压缩文件oos.writeObject(huffmanBytes);//这里我们以对象流的方式写入 赫夫曼编码,为了恢复原文件时使用//!!!一定要把赫夫曼编码也写入,要不然无法恢复oos.writeObject(huffmanCodes);}catch (Exception e){System.out.println(e.getMessage());}finally {try {is.close();os.close();oos.close();} catch (IOException ex) {System.out.println(ex);}}}

在这里插入图片描述

4.对文件进行解压

    //编写一个方法,进行解压public static void unzipFile(String zipFile,String dstFile){//文件输入流InputStream is = null;//创建输出流OutputStream os = null;//对象输入流ObjectInputStream ois = null;try {//创建文件输入流is = new FileInputStream(zipFile);//场景和is关联的对象输入流ois = new ObjectInputStream(is);//读取byte数组 huffmanBytesbyte [] huffmanBytes = (byte[]) ois.readObject();//读取赫夫曼编码表Map<Byte,String> huffmanCode = (Map<Byte,String>)ois.readObject();//解码byte [] bytes = decode(huffmanCode,huffmanBytes);//将bytes数组写入到目标文件os = new FileOutputStream(dstFile);//写出数据os.write(bytes);} catch (Exception e) {System.out.println(e.getMessage());}finally {try {os.close();ois.close();is.close();} catch (IOException e) {System.out.println(e.getMessage());}}}

在这里插入图片描述

赫夫曼编码可以压缩所有类型的文件,因为是通过字节进行压缩

在这里插入图片描述

完整代码

package tree.huffmantree.ZipFile;import java.io.*;
import java.util.*;public class HuffmanZipFile {public static void main(String[] args) {//测试压缩文件String srcFile = "D:\\薛艳春\\桌面\\新建文件夹 (3)\\薛艳春2.pdf";String dstFile = "D:\\薛艳春\\桌面\\新建文件夹 (3)\\薛艳春2.zip";zipFile(srcFile,dstFile);System.out.println("压缩成功~~");String zipFile = "D:\\薛艳春\\桌面\\新建文件夹 (3)\\dst.zip";String dstFile2 = "D:\\薛艳春\\桌面\\新建文件夹 (3)\\src2.png";//unzipFile(zipFile,dstFile2);}//编写一个方法,进行解压public static void unzipFile(String zipFile,String dstFile){//文件输入流InputStream is = null;//创建输出流OutputStream os = null;//对象输入流ObjectInputStream ois = null;try {//创建文件输入流is = new FileInputStream(zipFile);//场景和is关联的对象输入流ois = new ObjectInputStream(is);//读取byte数组 huffmanBytesbyte [] huffmanBytes = (byte[]) ois.readObject();//读取赫夫曼编码表Map<Byte,String> huffmanCode = (Map<Byte,String>)ois.readObject();//解码byte [] bytes = decode(huffmanCode,huffmanBytes);//将bytes数组写入到目标文件os = new FileOutputStream(dstFile);//写出数据os.write(bytes);} catch (Exception e) {System.out.println(e.getMessage());}finally {try {os.close();ois.close();is.close();} catch (IOException e) {System.out.println(e.getMessage());}}}//编写一个方法,进行文件压缩public static void zipFile(String srcFile, String dstFile){//创建输出流FileInputStream is = null;//文件输入流OutputStream os = null;ObjectOutputStream oos = null;try{is = new FileInputStream(srcFile);//创建一个和源文件大小一样的btyte[]byte[] b = new byte[is.available()];//读取文件is.read(b);//直接对源文件压缩byte[] huffmanBytes = huffmanZip(b);//创建文件的输出流,存放压缩文件os = new FileOutputStream(dstFile);//创建一个和文件输出流关联的ObjectOutPutStream对象流oos = new ObjectOutputStream(os);//把赫夫曼编码后的字节数组写入压缩文件oos.writeObject(huffmanBytes);//这里我们以对象流的方式写入 赫夫曼编码,为了恢复原文件时使用//!!!一定要把赫夫曼编码也写入,要不然无法恢复oos.writeObject(huffmanCodes);}catch (Exception e){System.out.println(e.getMessage());}finally {try {is.close();os.close();oos.close();} catch (IOException ex) {System.out.println(ex);}}}//完成数据解压//思路//1.将huffmanCodeBytes[-88,-65..]重写先转成赫夫曼编码对应的二进制字符串//2.赫夫曼编码对应的二进制字符串根据赫夫曼编码转成字符//编写一个方法,对压缩数据解码/**** @param huffmanCodes 赫夫曼编码表 map* @param huffmanBytes 需要解码的字节数组* @return 返回原来字符串对应的数组*/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++) {//判断是否是最后一个字节boolean flag = (i == huffmanBytes.length - 1);stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));}//System.out.println("赫夫曼 解码后 对应的二进制字符串:" + stringBuilder.toString());//把字符串按照指定的赫夫曼编码进行解码//把赫夫曼编码表的k,v进行调换;因为要反向查询Map<String,Byte> map = new HashMap<>();for (Map.Entry<Byte,String> entry : huffmanCodes.entrySet()){map.put(entry.getValue(),entry.getKey());}//System.out.println(map);//创建一个集合存放byteList<Byte> list = new ArrayList<>();for (int i = 0; i < stringBuilder.length();) {int count = 1; //小的计数器boolean flag = true;Byte b = null;while (flag){//1010100010111。。。。String 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中存放了所以的字符//把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需要补高位,如果是最后一个字节不需要补高位* @return*/private static String byteToBitString(boolean flag, byte b){//使用变量保存bint temp = b;//将b转成intif (flag){temp |= 256; //按位与256 1 0000 0000 | 0000 0001 =》1 0000 0001}String str = Integer.toBinaryString(temp);if (flag){return str.substring(str.length() - 8);}else {return str;}}//封装前面所写的,便于调用/**** @param bytes 原始字符串对应的字节数组* @return  返回的是经过赫夫曼编码处理后的字节数组(压缩后的数组)*/private static byte[] huffmanZip(byte [] bytes){//第一步:创建节点List<Node1> nodes = getNodes(bytes);//第二步:创建赫夫曼树Node1 huffmanTree = createHuffmanTree(nodes);//第三步:生成对应的赫夫曼编码(根据赫夫曼树)Map<Byte, String> hufumanCodes = getCondes(huffmanTree);//第四步:根据赫夫曼编码压缩,生成赫夫曼字节数组byte[] huffmanBytes = zip(bytes, hufumanCodes);return huffmanBytes;}//编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回赫夫曼编码压缩后的byte[]/**** @param bytes 原始的字符对应的byte[]* @param huffmanCodes  生成的赫夫曼编码表map* @return  返回赫夫曼编码处理后的byte[]* 举例:String content = "i like like like java do you like a java";* 返回的是字符串"10101000"。。。等等* =>对应byte[] huffmancodeBytes,即8位对应一个byte,放入到huffmanCodeBytes* huffmancodeBytes[0] = 10101000(补码) => byte [推导 10101000 => 10101000 -1 => 10100111(反码) => 11011000(原码)]* huffmancodeBytes[1] = -88*/private static byte[] zip(byte [] bytes, Map<Byte,String> huffmanCodes){//1.利用赫夫曼编码表将传进来的byte数组转成赫夫曼编码字符串StringBuilder stringBuilder = new StringBuilder();//遍历bytes数组for (byte b : bytes){stringBuilder.append(huffmanCodes.get(b));}//按照这个字符串发送肯定是变大了,不行,那么就要将字符串转成byte数组//System.out.println("赫夫曼 编码后 对应的二进制字符串:" + stringBuilder.toString());//统计返回的byte[] huffmanCodeBytes 长度//一句话搞定int len = (stringBuilder.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位对应一个byteString strByte;if (i+8 <= stringBuilder.length()){strByte = stringBuilder.substring(i,i+8);}else {strByte = stringBuilder.substring(i); //-88}//将StringByte转成byte数组放入到huffmanCodeByteshuffmanCodeBytes[index] = (byte) Integer.parseInt(strByte,2);index++;}return huffmanCodeBytes;}//生成赫夫曼树对应的赫夫曼编码//思路://1.将赫夫曼编码表存放在Map<Byte,String> 形式//32->01  97->100 100->11000等等static Map<Byte,String> huffmanCodes = new HashMap<>();//2.在生成赫夫曼编码表时需要拼接路径,创建Stringbuilder存储某个叶子节点的路径static  StringBuilder stringBuilder = new StringBuilder();//为了调用方便重载getCondesprivate static Map<Byte,String> getCondes(Node1 root){if (root == null){return null;}//处理rootgetCondes(root,"",stringBuilder);return huffmanCodes;}/*** 功能:将传入的node节点的所有叶子节点赫夫曼编码得到,并放入到赫夫曼集合中* @param node  传入节点* @param code  路径:左子节点是0,右子节点是1* @param stringBuilder 是用于拼接路径*/private static void getCondes(Node1 node,String code, StringBuilder stringBuilder){StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);//将传入的code加入到Stringbuilder2stringBuilder2.append(code);if (node != null){//判断当前node是叶子节点还是非叶子节点if (node.data == null){ //说明是非叶子节点//递归处理//向左递归getCondes(node.left,"0",stringBuilder2);//向右递归getCondes(node.right,"1",stringBuilder2);}else {//说明是叶子节点//就表示找到了某个叶子节点最后huffmanCodes.put(node.data,stringBuilder2.toString());}}}//前序遍历public static void preOrder(Node1 root){if (root != null){root.preOrder();}else {System.out.println("赫夫曼树为空");}}private  static List<Node1> getNodes(byte [] bytes){//1.创建ArrayListArrayList<Node1> node1s = new ArrayList<>();//遍历bytes 统计乜咯 byte出现的次数,存储每个byte出现的次数 -> mapMap<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()){node1s.add(new Node1(entry.getKey(),entry.getValue()));}return node1s;}//通过List创建赫夫曼树private static Node1 createHuffmanTree(List<Node1> nodes){while (nodes.size() > 1){//排序  从小到大Collections.sort(nodes);//取出第一颗、第二颗最小的二叉树Node1 leftNode = nodes.get(0);Node1 rightNode = nodes.get(1);//创建新的二叉树,新的二叉树没有数据,只有权值Node1 parent = new Node1(null,leftNode.weight + rightNode.weight);parent.left  = leftNode;parent.right = rightNode;//将0,1移除Listnodes.remove(leftNode);nodes.remove(rightNode);//parent加入Listnodes.add(parent);}//nodes最后剩余就是哈弗曼树的根节点return nodes.get(0);}
}
class Node1 implements Comparable<Node1>{Byte data; //存放数据 按照asciiint weight; //权值,表示字符出现的次数Node1 left;Node1 right;//前序遍历public void preOrder(){System.out.println(this);if (this.left != null){this.left.preOrder();}if (this.right != null){this.right.preOrder();}}@Overridepublic int compareTo(Node1 o) {return this.weight - o.weight;}public Node1(Byte data, int weight) {this.data = data;this.weight = weight;}@Overridepublic String toString() {return "Node1{" +"data=" + data +", weight=" + weight +'}';}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437534.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【转】Dynamics 365 Customer Engagement中插件的调试

微软动态CRM专家罗勇 &#xff0c;回复319或者20190319可方便获取本文&#xff0c;同时可以在第一间得到我发布的最新博文信息&#xff0c;follow me&#xff01;我的网站是 www.luoyong.me 。 本文主要根据官方的教程 Tutorial: Debug a plug-in 而写&#xff0c;使用的环境是…

数据结构 - 二叉排序树BST(创建、遍历、删除节点)

数组与链表区别&#xff1a; 二叉排序树的创建和遍历 代码实现 package tree.binarysorttree;public class BinarySortTreeDemo {public static void main(String []args){int [] arr {7,3,10,12,5,1,9};BinarySortTree binarySortTree new BinarySortTree();//循环添加节点…

【转】Dynamics 365中开发和注册插件介绍

是微软Dynamics 365 & Power Platform方面的工程师罗勇&#xff0c;也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面的微软最有价值专家(Microsoft MVP)&#xff0c;欢迎关注我的微信公众号 MSFTDynamics365erLuoYong &#xff0c;回复380或者201911…

数据结构- 平衡二叉树AVL树(左旋、右旋、双旋转)

第三棵树根节点的左子树高度为3右子树高度为1&#xff0c;相差为2大于1&#xff0c;所以不是平衡二叉树 //左旋转方法private void leftRotate(){//创建新的节点,以当前根节点的值Node newNode new Node(value);//把新的节点的左子树设置成当前节点的左子树newNode.left …

数据结构 - 多路搜索树(2-3树、b树、b+树、b*树)

如果二叉树的高度太高&#xff0c;速度还是比较慢 2-3树 实现步骤 数列&#xff1a;[16,24,12,32,26,34,10,8,28,38,20] 第一步&#xff1a;16直接放到一个2节点 第二步&#xff1a;24放到16后面 第三步&#xff1a;12不能放到16前面&#xff0c;要不然不满足2-3树变成4叉了…

【转】如何在 Visual Studio 2019 中连接中国版 Azure

导语 做国内项目很可能用到中国版的 Azure&#xff0c;即世纪互联运营的 azure.cn。然而 Visual Studio 2019 默认连接的是国际版的 Azure&#xff0c;如何添加中国版订阅呢&#xff1f; 差点996 6年前我曾经写过一篇《图解&#xff1a;如何在Visual Studio 2013中连接中国版…

数据结构- 图

矩阵&#xff1a;0表示不连通&#xff0c;1表示连通 图的遍历

【转】掌握Azure订阅的关键概念和术语

Azure的订阅结构可以让管理员分担结算和资源管理的责任。但首先&#xff0c;他们需要了解账户和部门等概念。 通过Microsoft企业协议&#xff0c;Azure订阅遵循分层结构来隔离用户角色和权限。围绕这些订阅的术语有时难以理解&#xff0c;特别是Azure部门和帐户相关的术语。 …

算法 - 二分查找(非递归实现二分查找)

package Algorithm.binarysearchnorecursion;public class BinaaySearchNoRecur {public static void main(String[] args) {int arr [] {1,2,3,4,5};int b binarySearch(arr, 0);System.out.println(b);}//二分查找的非递归实现public static int binarySearch(int [] arr, …

【转】Windows Azure的账户体系

Windows Azure提供了分级的账户体系&#xff0c;理解并灵活使用这种账户体系能为用户的业务提供非常切实的帮助。 不同类型的客户和账户体系 Windows Azure将客户分为两种基本类型&#xff1a;标准客户和企业协议客户。企业协议客户&#xff08;EnterpriseAgreement Customer…

算法- 分治算法(实现汉诺塔)

package Algorithm.dac;public class Hannoitower {public static void main(String []args){hannoiTower(5,A, B, C);}//汉诺塔移动方法//使用分治算法public static void hannoiTower(int num, char a, char b, char c){//如果只有一个盘if (num 1){System.out.println(&quo…

【转】关于Azure存储账户

概述 Azure 存储帐户是一个在 Azure存储服务中使您能够访问的安全帐户。您的存储帐户为您的数据&#xff0c;提供了唯一的命名空间&#xff0c;默认情况下&#xff0c;它是仅供您帐户的所有者使用的。 有两种类型的存储帐户&#xff1a; 标准存储帐户包括 Blob、表和队列存储…

算法 - 动态规划(0-1背包问题)

推出公式&#xff1a; 第三个就是&#xff1a;让上一次的和&#xff08;新加入商品容量(总空间-新加入的商品容量&#xff09;也就是剩余空间的最大值&#xff0c;剩余空间的最大值去上一层找&#xff09; package Algorithm.dac.knapsack;public class KnapsackProblem {publ…

【转】关于Azure存储账户(2)

接上一篇&#xff1a; 存储帐户终结点 您在 Azure存储中储存的每个对象都有一个唯一的 URL 地址&#xff1b;存储帐户名称构成该地址的子域。针对每一个服务的域名加上子域名&#xff0c;构成您的存储帐户一个终结点。 例如&#xff0c;如果您的存储帐户被命名为mystorageacco…

算法 - KMP算法(字符串匹配)

百度的一个图文介绍http://www.sohu.com/a/336648975_453160 package Algorithm.kmp;import java.util.Arrays;public class KMPViolenceMatch {public static void main(String[] args) {String str1 "BBC ABCDAB ABCDABCDABDE";String str2 "ABCDABD"…

算法 - 贪心算法(集合覆盖问题求解)

1.穷举法 2.贪心算法 遍历集合的key&#xff0c;用当前key的value和allAreas去取交集&#xff09;&#xff0c;然后和&#xff08;maxKey和allAreas的交集&#xff09;比较大小&#xff0c;如果当前key的结合size大就把当前key赋给maxkey&#xff0c;循环5次后退出循环&…

【转】使用多个 Azure 订阅(按照订阅收费)

可以在单个 Azure 帐户下创建多个订阅。 这对于企业尤其有用&#xff0c;因为_访问控制_和_计费_在订阅级别发生&#xff0c;而不是在帐户级别发生。 Azure AD 租户和订阅存在多对一的信任关系&#xff1a;一个租户可以与多个 Azure 订阅相关联&#xff0c;但是每个订阅只与一个…

算法 - 普里姆算法(修路问题求解)

从A开始&#xff0c;A作为定点&#xff0c;找到与A相连并且未被处理(不在顶点集合中)的进行处理&#xff0c;找到权值最小的并加入集合&#xff0c;A-C[7]、A-G[2]、A-B[5]&#xff0c;最小的是A-G[2],所以把G加入集合&#xff0c;这里是有A-G的连接的。 然后把A、G作为顶点&a…

【转】什么是 Azure 资源管理器

什么是 Azure 资源管理器&#xff1f; 2020/09/01 Azure 资源管理器是 Azure 的部署和管理服务。 它提供了一个管理层&#xff0c;用于在 Azure 帐户中创建、更新和删除资源。 部署后&#xff0c;可以使用访问控制、锁和标记等管理功能来保护和组织资源。 若要了解 Azure 资源…

算法 -克鲁斯卡尔算法

如果形成回路则不选择 文字描述 加入的边的两个顶点不能都指向同一个终点&#xff0c;否则会构成回路