package com.haiyang.datastructure.huffmancode;
import java.util.*;
/**
* @author haiYang
* @create 2022-01-19 15:31
*/
public class HuffmanCode {
public static void main(String[] args) {
String content = "The most heated arguments often occur between people on opposite ends of the spectrum";
//对应的字节数组为[84, 104, 101, 32, 109, 111, 115, 116, 32, 104, 101, 97, 116, 101, 100, 32, 97, 114, 103, 117, 109, 101, 110, 116, 115, 32, 111, 102, 116, 101, 110, 32, 111, 99, 99, 117, 114, 32, 98, 101, 116, 119, 101, 101, 110, 32, 112, 101, 111, 112, 108, 101, 32, 111, 110, 32, 111, 112, 112, 111, 115, 105, 116, 101, 32, 101, 110, 100, 115, 32, 111, 102, 32, 116, 104, 101, 32, 115, 112, 101, 99, 116, 114, 117, 109]
byte[] contentBytes = content.getBytes();//将给定的字符串转化为字节数组
System.out.println(Arrays.toString(contentBytes));
System.out.println(contentBytes.length);
List<Node> nodes = getNodes(contentBytes);//调用getCodes方法将字节数组转化为结点类型
System.out.println("nodes=" + nodes);
//nodes=[Node{data=32, weight=13}, Node{data=97, weight=2}, Node{data=98, weight=1}, Node{data=99, weight=3}, Node{data=100, weight=2}, Node{data=101, weight=14}, Node{data=102, weight=2}, Node{data=103, weight=1}, Node{data=104, weight=3}, Node{data=105, weight=1}, Node{data=108, weight=1}, Node{data=109, weight=3}, Node{data=110, weight=5}, Node{data=111, weight=8}, Node{data=112, weight=5}, Node{data=114, weight=3}, Node{data=115, weight=5}, Node{data=84, weight=1}, Node{data=116, weight=8}, Node{data=117, weight=3}, Node{data=119, weight=1}]
System.out.println("创建哈夫曼树");
Node huffmanTree = createHuffmanTree(nodes);
System.out.println("先序遍历");
huffmanTree.preOrder();
Map<Byte, String> codes = getCodes(huffmanTree);//获取哈夫曼编码
System.out.println(codes);
}
//用于存储哈夫曼编码
static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
//用于拼接哈夫曼编码
static StringBuilder stringBuilder = new StringBuilder();
/**
* 获取哈夫曼编码
* @param node 根结点
* @param code 编码 左0右1
* @param stringBuilder 用于拼接哈夫曼编码
*/
public static void getCodes(Node node, String code, StringBuilder stringBuilder) {
StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
stringBuilder1.append(code);
if(node != null){
if(node.data == null){ //代表非叶子结点
getCodes(node.left,"0",stringBuilder1);
getCodes(node.right,"1",stringBuilder1);
}else{
//如果是叶子结点,代表该字节的编码已拼接完成,加入到Map中
huffmanCodes.put(node.data,stringBuilder1.toString());
}
}
}
/**
* 方法重载
* 将字节编码存储到Map中
* @param root
* @return
*/
public static Map<Byte,String> getCodes(Node root){
if(root ==null){
return null;
}
getCodes(root.left,"0",stringBuilder);
getCodes(root.right,"1",stringBuilder);
return huffmanCodes;
}
public static void preOrder(Node root) {
if (root != null) {
root.preOrder();
} else {
System.out.println("树为空,无法遍历");
}
}
//将字节数组转化为结点类型
public static List<Node> getNodes(byte[] bytes) {
ArrayList<Node> nodes = new ArrayList<>();//储存结点
HashMap<Byte, Integer> map = new HashMap<>();//储存每种字节出现的次数
for (byte b : bytes) {
//遍历数组记录每种字节出现的次数
Integer count = map.get(b);
if (count == null) {
map.put(b, 1);
} else {
map.put(b, count + 1);
}
}
for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
//遍历Map将每种字节及其出现的次数转化为结点
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
/**
* 创建哈夫曼树
* 1、首先将结点列表nodes按weight升序排序
* 2、从结点列表nodes中选取两个最小的结点构建一棵树,其根节点的weight为两个最小结点的和
* 3、将结点列表中的已选取两个最小的结点移除,并将新构建的树的根节点加入到结点列表中
* 4、重复1 2 3 步骤,直到剩一个结点,此节点为已构建好的哈夫曼树的根节点
* @param nodes
* @return
*/
public static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
//按weight升序排序
Collections.sort(nodes);
//从结点列表nodes中选取两个最小的结点
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
//通过选取的结点构建一棵树,其根节点的weight为两个最小结点的和
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);
}
}
class Node implements Comparable<Node> {
//在给定的序列中,每种字节是一个data,weight代表每种字节出现的次数
Byte data;
int weight;
Node left;
Node right;
public Node(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", weight=" + weight +
'}';
}
//自定义比较器,通过weight进行升序排序
@Override
public int compareTo(Node o) {
return this.weight - o.weight;
}
//先序遍历 用于查看构建的大顶堆是否正确
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
}
数据结构之哈夫曼树及编码的实现(java)
于 2022-01-20 16:15:55 首次发布