package com.source.eight;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class HuffmanCompress {
private static Map<Byte,String> hufumanMap = new HashMap<Byte,String>(); //存放赫夫曼编码
private static StringBuilder sb = new StringBuilder(); //拼接路径
public static void main(String[] args) {
HuffmanCompress hc = new HuffmanCompress();
String contant = "i like like like java do you like a java absddddd";
System.out.println("原始数据 " + contant);
byte[] bys = contant.getBytes();
//压缩
byte[] zips = hc.zipMethod(contant.getBytes());
System.out.println("压缩后 " + Arrays.toString(zips));
//解压
String resZip = hc.resZip(zips, bys);
System.out.println("解压后 " + resZip);
}
//封装一个方法用于解码赫夫曼
public String resZip(byte[] zips,byte[] bys) {
//返回补码字符串
String codes = byteToBitString(zips);
//将赫夫曼字符串解码成String字符串
String resString = bitStringtoString(codes, bys);
//返回
return resString;
}
//将赫夫曼字符串解码成String字符串
private String bitStringtoString(String codes,byte[] bys) {
//1.将bys数组用list集合保存
List<CompressNode> nodes = getNodes(bys);
//2.将nodes集合创建成赫夫曼树
CompressNode root = createHuffmanTree(nodes);
//3.使用map集合保存赫夫曼编码
Map<Byte,String> maps = getCodes(root);
//4.遍历codes集合
StringBuilder stringBuilder = new StringBuilder();
int size = codes.length();
int buChang = 2;
Set<Byte> keySet = maps.keySet();
for (int i = 0; i < size;i += buChang) {
for (Byte key : keySet) {
String value = maps.get(key);
buChang = value.length();
if (i+buChang <= size) {
String res = codes.substring(i, i+buChang);
if (res.equals(value)) {
byte b = key;
stringBuilder.append((char)b);
break;
}
}
}
}
return stringBuilder.toString();
}
//解压赫夫曼压缩数组成String字符
public String byteToBitString(byte[] zips) {
StringBuilder stringBuilder = new StringBuilder();
//遍历zips数组
int len = zips.length;
for(int i = 0; i < len; i++) {
if (i+1 == zips.length) {
String complement = byteToBitComplement(zips[i]);
int count = 0;
for(int j = 0; j < complement.length(); j++) {
if (complement.charAt(j) != '0') {
count = j;
break;
}
}
complement = complement.substring(count);
stringBuilder.append(complement);
}else {
stringBuilder.append(byteToBitComplement(zips[i]));
}
}
return stringBuilder.toString();
}
/**
*
* @param 将一个字节 转换为对应的补码
* @return
*/
private String byteToBitComplement(byte b) {
String complement = "00000000";
//1.判断b的正负
if (b > 0) {
//补码和原码一致所有直接除2
StringBuilder sb = new StringBuilder();
int n = 0;
while (b > 0) {
n = b % 2;
sb.append(n);
b /= 2;
}
while (sb.length() < 8) {
sb.append("0");
}
//sb反转
sb.reverse();
complement = sb.toString();
}else if (b < 0){
b = (byte) Math.abs(b);
//b小于0,先获取b的原码再获取反码最后获取补码
//1.获取原码
StringBuilder sb = new StringBuilder();
int n = 0;
while (b > 0) {
n = b % 2;
sb.append(n);
b /= 2;
}
while (sb.length() < 8) {
sb.append("0");
}
//sb反转
sb.reverse();
String yuanMa = sb.toString();
//通过原码获取反码
StringBuilder sb1 = new StringBuilder();
for(int i = 0; i < 8;i++) {
char c = yuanMa.charAt(i);
if (c == '1') {
sb1.append('0');
}else {
sb1.append('1');
}
}
//通过反码获取补码
StringBuilder sb2 = new StringBuilder();
String fanMa = sb1.toString();
fanMa = fanMa.substring(0, fanMa.length()-1) + (Integer.parseInt(fanMa.substring(fanMa.length()-1)) + 1);
//循环遍历获取补码
for (int i = 7 ; i >= 0;i--) {
if (fanMa.charAt(i) == '2') {
sb2.append("0");
fanMa = fanMa.substring(0, i-1) + (Integer.parseInt(fanMa.substring(i-1,i)) + 1) + fanMa.substring(i);
}else {
sb2.append(fanMa.charAt(i));
}
}
sb2.reverse();
complement = sb2.toString();
}
return complement;
}
//封装一个方法,返回赫夫曼压缩数组
public byte[] zipMethod(byte[] bys) {
//1.将bys数组用list集合保存
List<CompressNode> nodes = getNodes(bys);
//2.将nodes集合创建成赫夫曼树
CompressNode root = createHuffmanTree(nodes);
//3.使用map集合保存赫夫曼编码
Map<Byte,String> maps = getCodes(root);
//4.返回压缩后的数组
byte[] zip = zip(bys, maps);
return zip;
}
//返回赫夫曼处理的编码
private static byte[] zip(byte[] bys,Map<Byte,String> hufumanMap) {
//1.获取赫夫曼编码
StringBuilder sb = new StringBuilder();
for (byte b : bys) {
sb.append(hufumanMap.get(b));
}
//2.将编码8个为一位转换为字节
//确定tempByte【】的大小
int len;
if (sb.length() % 8 == 0) {
len = sb.length() / 8;
}else {
len = sb.length() / 8 + 1;
}
byte[] tempByte = new byte[len];
String str = "";
int index = 0;//记录第几个byte
//for循环遍历sb
for (int i = 0; i <sb.length(); i += 8) {
if (i+8 < sb.length()) {
str = sb.substring(i, i+8);
}else {
str = sb.substring(i);
}
//(byte)Integer.parseInt(str, 2);
tempByte[index++] = (byte)Integer.parseInt(str, 2);
}
return tempByte;
}
//生成赫夫曼编码 重载
private static Map<Byte, String> getCodes(CompressNode node) {
if (node == null) {
return null;
}
getCodes(node, "", sb);
return hufumanMap;
}
//生成赫夫曼编码
private static void getCodes(CompressNode node,String code,StringBuilder sb) {
StringBuilder stringBuilder = new StringBuilder(sb);
stringBuilder.append(code);
if (node.data == null) {
//向左遍历
getCodes(node.left, "0", stringBuilder);
//向右遍历
getCodes(node.right, "1", stringBuilder);
}else {
//保存叶子节点到map中
hufumanMap.put(node.data, stringBuilder.toString());
}
}
//创建赫夫曼树
private CompressNode createHuffmanTree(List<CompressNode> nodes) {
while (nodes.size() > 1) {
//排序
Collections.sort(nodes);
//选择 ndoes 0 1 两个节点
CompressNode leftNode = nodes.get(0);
CompressNode rightNode = nodes.get(1);
//创建新的节点
CompressNode newNode = new CompressNode(null, leftNode.weight + rightNode.weight);
newNode.left = leftNode;
newNode.right = rightNode;
//将新节点加入nodes中
nodes.add(newNode);
//删除左右两个节点
nodes.remove(leftNode);
nodes.remove(rightNode);
}
return nodes.get(0);
}
//将byte[] 转换为list集合
private List<CompressNode> getNodes(byte[] bytes) {
//统计bytes数组里的元素
Map<Byte,Integer> map = new HashMap<Byte,Integer>();
for (byte b : bytes) {
//根据key获取值
Integer res = map.get(b);
if (res == null) {
map.put(b, 1);
}else {
res += 1;
map.put(b, res);
}
}
//创建List集合
List<CompressNode> nodes = new ArrayList<CompressNode>();
//遍历map集合创建CompressNode节点并添加到nodes集合
Set<Byte> keySet = map.keySet();
for (Byte b : keySet) {
CompressNode compressNode = new CompressNode(b, map.get(b));
nodes.add(compressNode);
}
//测试 输出nodes节点
//System.out.println(nodes);
//从小到大排序
//Collections.sort(nodes);
//再次输出
//System.out.println(nodes);
return nodes;
}
}
class CompressNode implements Comparable<CompressNode>{
public Byte data;
public Integer weight;
public CompressNode left;
public CompressNode right;
public CompressNode(Byte data, Integer weight) {
this.data = data;
this.weight = weight;
}
@Override
public int compareTo(CompressNode o) {
return this.weight - o.weight;
}
@Override
public String toString() {
return "CompressNode [data=" + data + ", weight=" + weight + "]";
}
//前序遍历
public void preSort() {
System.out.println(this);
if(this.left != null) {
this.left.preSort();
}
if(this.right != null) {
this.right.preSort();
}
}
}