package cn.mrlij.tree.huffman;
import java.util.*;
import java.util.Map.Entry;
/**
* 实现生成哈夫曼树对应的哈夫曼编码
*/
public class HuffmanCode {
public static void main(String[] args) {
String str = "i like like like java do you like a java";
byte b[] = str.getBytes();
//System.out.println(b.length);
List<Node> nodes = getNodes(b);
System.out.println(nodes);
Node node = createHuffman(nodes);
node.preOrder();
getCodes(node,"",sb);
System.out.println(huffmanCodes);
System.out.println(Arrays.toString(zip(b,huffmanCodes)));
}
static Map<Byte,String> huffmanCodes = new HashMap<>();//用于存放字符所对应的哈夫曼编码
static StringBuilder sb = new StringBuilder();//用于拼接对应的哈夫曼编码
public static void getCodes(Node node,String code,StringBuilder stringBuilder){
//构建StringBuilder
StringBuilder s = new StringBuilder(stringBuilder);
s.append(code);
if(node!=null){
//数据为空时,说明是非叶子节点
if(node.data == null){
getCodes(node.left,"0",s);
getCodes(node.right,"1",s);
}else {
//说明是叶子节点,将他放入map中
huffmanCodes.put(node.data,s.toString());
}
}
}
//对照生成的哈夫曼编码表,将原始数组转换为压缩后的字节数组
public static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
//将字节数组转换为对应字符串
StringBuilder stringBuilder = new StringBuilder();
for (byte bt : bytes){
stringBuilder.append(huffmanCodes.get(bt));
}
//转换之后的数组进行处理=>计算将要创建的字节数组的长度
int len;
if(stringBuilder.length()%8==0){
len = stringBuilder.length()/8;
}else{
len = stringBuilder.length()/8+1;
}
byte[] b = new byte[len];
int index = 0;
//字节数组创建之后,将对应的字符串按照8位转成一个字节
for(int i =0;i<stringBuilder.length();i+=8){
String str;
//可能出现的情况是,不能保证字符串的长度刚好被8整除
if(i+8>stringBuilder.length()){
str= stringBuilder.substring(i);
}else{
str= stringBuilder.substring(i,i+8);
}
b[index] = (byte) Integer.parseInt(str, 2);
index++;
}
return b;
}
//构建哈夫曼树
public static List<Node> getNodes(byte[] b){
List<Node> nodes = new ArrayList<>();
Map<Byte,Integer> map = new HashMap<>();
//遍历字节数组
for(byte bt : b) {
Integer count = map.get(bt);
if(count!=null) {
map.put(bt, count+1);
}else {
map.put(bt,1);
}
}
//依次创建树
for(Entry<Byte,Integer> entry : map.entrySet()) {
Node node = new Node(entry.getKey(),entry.getValue());
nodes.add(node);
}
return nodes;
}
public static Node createHuffman(List<Node> nodes) {
while(nodes.size()>1) {
//首先进行排序
Collections.sort(nodes);
//拿出集合中前两个最小的
Node left = nodes.get(0);
Node right = nodes.get(1);
//创建节点
Node parent = new Node(null,left.val+right.val);
parent.left = left;
parent.right = right;
//将两个节点从集合移除
nodes.remove(left);
nodes.remove(right);
//将父节点加入集合
nodes.add(parent);
}
return nodes.get(0);
}
public static void preOrder(Node root) {
if(root!=null) {
root.preOrder();
}else {
System.out.println("tree为空");
}
}
}
//创建Node,存放数据和权值
class Node implements Comparable<Node>{
Byte data;//具体的字符
int val;//表示字符出现的次数
Node left;//左节点
Node right;//右节点
@Override
public String toString() {
return "Node [data=" + data + ", val=" + val + "]";
}
public Node(Byte data, int val) {
this.data = data;
this.val = val;
}
//前序遍历
public void preOrder() {
System.out.println(this);
if(this.left!=null) {
this.left.preOrder();
}
if(this.right!=null) {
this.right.preOrder();
}
}
@Override
public int compareTo(Node o) {
return this.val-o.val;
}
}