packagecom.zhao.algorithm.tree;import java.io.*;import java.util.*;/*** AUTHOR :zhao
* 日期:2020/2/17 17:42
* 赫夫曼编码 压缩与解压缩, 他是无损压缩*/
public classHuffmanCode {public static voidmain(String[] args) {//start 赫夫曼编码 解码
String msg="Man proposes,God disposes.";byte[] bytes =msg.getBytes();
System.out.println("压缩前长度 :"+bytes.length);//进行赫夫曼编码压缩
byte[] b =huffmanZip(bytes);
System.out.println("压缩后长度 :"+b.length);//使用赫夫曼编码进行解码
byte[] newBytes =decode(huffCodes,b);
System.out.println(newString(newBytes));//end ---------// //user.dir指定了当前的路径//String src=System.getProperty("user.dir")+"\\haffman.bmp";// //这里的压缩名后缀随便起//String dst="haffman.god";//try {//zipFile(src, dst);//} catch (IOException e) {//e.printStackTrace();//}//try {//unZip(dst, "haffman2.bmp");//} catch (Exception e) {//e.printStackTrace();//}
}/*** 文件的解压
*@paramsrc
*@paramdst
*@throwsException*/
public static void unZip(String src,String dst) throwsException {//创建一个输入流
InputStream is = newFileInputStream(src);
ObjectInputStream ois= newObjectInputStream(is);//读取byte数组
byte[] b = (byte[]) ois.readObject();//读取赫夫曼编码表
Map codes = (Map) ois.readObject();
ois.close();
is.close();//解码
byte[] bytes =decode(codes, b);//创建一个输出流
OutputStream os = newFileOutputStream(dst);//写出数据
os.write(bytes);
os.close();
}/*** 压缩文件
*@paramsrc 源文件
*@paramdst 压缩后的文件
*@throwsIOException*/
public static void zipFile(String src,String dst) throwsIOException {//创建一个输入流
InputStream is = newFileInputStream(src);//创建一个和输入流指向的文件大小一样的byte数组//available 返回从此输入流中可以读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。
byte[] b = new byte[is.available()];//读取文件内容
is.read(b);
is.close();//使用赫夫曼编码进行编码
byte[] byteZip =huffmanZip(b);//输出流
OutputStream os = newFileOutputStream(dst);
ObjectOutputStream oos= newObjectOutputStream(os);//把压缩后的byte数组写入文件
oos.writeObject(byteZip);//把赫夫曼编码表写入文件
oos.writeObject(huffCodes);
oos.close();
os.close();
}/*** 使用指定的赫夫曼编码表进行解码
*
*@paramhuffCodes
*@paramb
*@return
*/
private static byte[] decode(Map huffCodes, byte[] bytes) {
StringBuilder sb= newStringBuilder();//把byte数组转为一个二进制的字符串
for (int i = 0; i < bytes.length; i++) {byte b =bytes[i];//是否是最后一个。
boolean flag = (i == bytes.length - 1);
sb.append(byteToBitStr(!flag, b));
}//把字符串按照指定的赫夫曼编码进行解码//把赫夫曼编码的键值对进行调换
Map map = new HashMap<>();for (Map.Entryentry : huffCodes.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}//创建一个集合,用于存byte
List list = new ArrayList<>();//处理字符串
for (int i = 0; i
Byte b= null;//截取出一个byte
while(flag) {
String key= sb.substring(i, i +count);
b=map.get(key);if (b == null) {
count++;
}else{
flag= false;
}
}
list.add(b);
i+=count;
}//把集合转为数组
byte[] b = new byte[list.size()];for (int i = 0; i < b.length; i++) {
b[i]=list.get(i);
}returnb;
}private static String byteToBitStr(boolean flag, byteb) {int temp =b;if(flag) {
temp|= 256;
}
String str=Integer.toBinaryString(temp);if(flag) {return str.substring(str.length() - 8);
}else{returnstr;
}
}/*** 进行赫夫曼编码压缩的方法
*
*@parambytes
*@return
*/
private static byte[] huffmanZip(byte[] bytes) {//先统计每一个byte出现的次数,并放入一个集合中
List nodes =getNodes(bytes);//创建一颗赫夫曼树
HuffmanNode tree =createHuffmanTree(nodes);//创建一个赫夫曼编码表
Map huffCodes =getCodes(tree);//编码
byte[] b =zip(bytes, huffCodes);returnb;
}/*** 把byte数组转为node集合
*
*@parambytes
*@return
*/
private static List getNodes(byte[] bytes) {
List nodes = new ArrayList<>();//存储每一个byte出现了多少次。
Map counts = new HashMap<>();//统计每一个byte出现的次数
for (byteb : bytes) {
Integer count=counts.get(b);if (count == null) {
counts.put(b,1);
}else{
counts.put(b, count+ 1);
}
}//把每一个键值对转为一个node对象
for (Map.Entryentry : counts.entrySet()) {
nodes.add(newHuffmanNode(entry.getKey(), entry.getValue()));
}returnnodes;
}/*** 进行赫夫曼编码
*
*@parambytes
*@paramhuffCodes
*@return
*/
private static byte[] zip(byte[] bytes, MaphuffCodes) {
StringBuilder sb= newStringBuilder();//把需要压缩的byte数组处理成一个二进制的字符串,其实就是他每个字母的赫夫曼路径
for (byteb : bytes) {
sb.append(huffCodes.get(b));
}//定义长度
intlen;if (sb.length() % 8 == 0) {
len= sb.length() / 8;
}else{
len= sb.length() / 8 + 1;
}//用于存储压缩后的byte
byte[] by = new byte[len];//记录新byte的位置
int index = 0;for (int i = 0; i < sb.length(); i += 8) {
String strByte;if (i + 8 >sb.length()) {
strByte=sb.substring(i);
}else{
strByte= sb.substring(i, i + 8);
}byte byt = (byte) Integer.parseInt(strByte, 2);
by[index]=byt;
index++;
}returnby;
}//用于临时存储路径
static StringBuilder sb = newStringBuilder();//用于存储赫夫曼编码
static Map huffCodes = new HashMap<>();/*** 根据赫夫曼树获取赫夫曼编码
*
*@paramtree
*@return
*/
private static MapgetCodes(HuffmanNode tree) {if (tree == null) {return null;
}
getCodes(tree.left,"0", sb);
getCodes(tree.right,"1", sb);returnhuffCodes;
}/*** 获取赫夫曼 编码中的 数据 eg:b 和数据出现的路径 huffCodes
*
*@paramnode 树节点
*@paramcode 0 左节点 1右节点
*@paramsb 获取这个节点中的数据要走的路径*/
private static voidgetCodes(HuffmanNode node, String code, StringBuilder sb) {
StringBuilder sb2= newStringBuilder(sb);
sb2.append(code);if (node.data == null) {
getCodes(node.left,"0", sb2);
getCodes(node.right,"1", sb2);
}else{
huffCodes.put(node.data, sb2.toString());
}
}/*** 创建赫夫曼树
*
*@paramnodes
*@return
*/
private static HuffmanNode createHuffmanTree(Listnodes) {while (nodes.size() > 1) {//排序
Collections.sort(nodes);//取出两个权值最低的二叉树
HuffmanNode left = nodes.get(nodes.size() - 1);
HuffmanNode right= nodes.get(nodes.size() - 2);//创建一颗新的二叉树
HuffmanNode parent = new HuffmanNode(null, left.weight +right.weight);//把之前取出来的两颗二叉树设置为新创建的二叉树的子树
parent.left =left;
parent.right=right;//把前面取出来的两颗二叉树删除
nodes.remove(left);
nodes.remove(right);//把新创建的二叉树放入集合中
nodes.add(parent);
}return nodes.get(0);
}
}