在电讯通信中的经典应用。
数据无损压缩。
使用哈夫曼编码;任何编码不可能是另一个编码的前缀编码,不会咋成匹配的多译性。空格01.
package com;
import java.util.*;
public class HuffmanCode {
public static void main(String[] args) {
//
String str="I like like like java do you like a java";
byte[] bytes = str.getBytes();
System.out.println(bytes.length);
List<Node> nodes = getNodes(bytes);
System.out.println(nodes);
System.out.println("---------------");
Node root = createHuffmanTree(nodes);
System.out.println("------------------");
Map<Byte, String> codes = getCodes(root);
System.out.println(codes);
}
//为了方便调用,重载
private static Map<Byte,String>getCodes(Node root){
if(root==null)
return null;
else{
getCodes(root.left,"0",stringBuilder);
getCodes(root.right,"1",stringBuilder);
}
return huffmanCode;
}
//创建对应的哈夫曼编码表
//1.将哈夫曼编码表存放在Map<Byte,Integer> 形式
static Map<Byte,String> huffmanCode=new HashMap<>();
//2.在生成哈夫曼编码时,需要去拼接路径,定义一个StringBuilder,存储某个叶子结点的路径
static StringBuilder stringBuilder=new StringBuilder();
//功能:将传入的node结点的而所有叶子结点哈夫曼编码存放到huffmanCode集合中
//code:路径 左0右1
//StringBuilder:用拼接路径
private static void getCodes(Node node,String code,StringBuilder stringBuilder){
StringBuilder stringBuilder2=new StringBuilder(stringBuilder); //?????为啥不能直接用????
stringBuilder2.append(code);
if(node!=null){
//判断是叶子结点还是非叶子结点
if(node.data==null){ //非叶子结点
//向左递归
getCodes(node.left,"0",stringBuilder2);
getCodes(node.right,"1",stringBuilder2);
}else{ //说明是叶子结点
//表示找道某个叶子结点的最后
huffmanCode.put(node.data,stringBuilder2.toString());
}
}
}
//bytes:接受字节数组
//返回List的Node集合
private static List<Node> getNodes(byte[] bytes){
//创建一个ArrayList
ArrayList<Node> nodes=new ArrayList<Node>();
//遍历byte,统计每个字符出现的次数,map[key,value]
Map<Byte,Integer> counts=new HashMap<>();
for(byte b:bytes){
Integer count=counts.get(b);
if(count==null){
counts.put(b,1);
}else{
counts.put(b,count+1); //之后添加的会覆盖之前添加的
}
}
//把每个键值对转成Node对象,并加入到nodes集合中
for(Map.Entry<Byte,Integer> entry:counts.entrySet()){
nodes.add(new Node(entry.getKey(),entry.getValue()));
}
return nodes;
}
private static Node createHuffmanTree(List<Node> nodes){
while(nodes.size()>1){
Collections.sort(nodes);
Node leftNode=nodes.get(0);
Node rightNode=nodes.get(1);
Node parent=new Node(null,leftNode.weight+rightNode.weight);
parent.left=leftNode;
parent.right=rightNode;
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
}
//最后的结点就是哈夫曼树的根节点
return nodes.get(0);
}
//前序遍历
private static void preOrder(Node root){
if(root==null)
{
System.out.println("树为空");
return;
}
else{
root.preOrder();
}
}
}
// 创建Node,存数据和权值
class Node implements Comparable<Node> {
Byte data; // 存放字符本身
int weight; // 存放数据出现的次数
Node left;
Node right;
public Node(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
@Override
public int compareTo(Node o) {
// 从小到大
return this.weight - o.weight;
}
@Override
public String toString() {
return "Node{" + "data=" + data + ", weight=" + weight + '}';
}
// 遍历
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
}