哈夫曼树
import java.util.ArrayList;
import java.util.Collections;
public class HuffmanTree {
public static void main(String[] args) {
int[] arr = {13, 7, 8, 3, 29, 6, 1};
Node root = creatHuffmanTree(arr);
preOrderTree(root);
//Node{weight=67, flag=false}
//Node{weight=29, flag=true}
//Node{weight=38, flag=false}
//Node{weight=15, flag=false}
//Node{weight=7, flag=true}
//Node{weight=8, flag=true}
//Node{weight=23, flag=false}
//Node{weight=10, flag=false}
//Node{weight=4, flag=false}
//Node{weight=1, flag=true}
//Node{weight=3, flag=true}
//Node{weight=6, flag=true}
//Node{weight=13, flag=true}
}
//创建一颗哈夫曼树
public static Node creatHuffmanTree(int[] arr) {
ArrayList<Node> arrayList = new ArrayList<>();
for (int no : arr) {
Node node = new Node(no,true);
arrayList.add(node);
}
while (arrayList.size() > 1) {
//由小到大排序
Collections.sort(arrayList);
//取出最小的两个节点
Node leftNode = arrayList.get(0);
Node rightNode = arrayList.get(1);
//构建一个新的二叉树
Node parent = new Node(leftNode.weight + rightNode.weight,false);
parent.left = leftNode;
parent.right = rightNode;
//修改集合元素
arrayList.remove(leftNode);
arrayList.remove(rightNode);
arrayList.add(parent);
}
return arrayList.get(0);
}
//前序遍历
public static void preOrderTree(Node root) {
if (root != null) {
root.preOrder();
}else{
System.out.println("该树为空");
}
}
}
public class Node implements Comparable<Node> {
int weight;
boolean flag;//是否为叶子节点
Node left;
Node right;
public Node(int weight, boolean flag) {
this.weight = weight;
this.flag = flag;
}
@Override
public String toString() {
return "Node{" +
"weight=" + weight +
", flag=" + flag +
'}';
}
@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();
}
}
}
哈夫曼编码
编码流程:
数据压缩
public class Node {
Byte date;
int weight;
Node left;
Node right;
public Node() {
}
public Node(Byte date, int weight) {
this.date = date;
this.weight = weight;
}
@Override
public String toString() {
return "Node{" + "date=" + date + ",weight=" + weight + '}';
}
//前序遍历
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
}
import java.util.*;
public class HuffmanTree_Compress {
public static void main(String[] args) {
String s = "i like like like java do you like a java";
//压缩前的字节数组
byte[] bt1 = s.getBytes();
System.out.println(Arrays.toString(bt1) + "---" + bt1.length);
/*
[105, 32, 108, 105, 107, 101, 32, 108, 105, 107, 101, 32, 108, 105, 107,
101, 32, 106, 97, 118, 97, 32, 100, 111, 32, 121, 111, 117, 32, 108,
105, 107, 101, 32, 97, 32, 106, 97, 118, 97]---40
*/
/**
* 压缩
*/
//1..获取Node节点的集合
ArrayList<Node> nodes = getNodes(bt1);
//2.生成哈夫曼树
Node root = createHuffmanTree(nodes);
//3.生成编码表
HashMap<Byte, String> encodingTable = getCode(root);
System.out.println(encodingTable);
//{32=000, 97=011, 100=1000, 117=1010, 101=1111, 118=010, 121=1001, 105=1101, 106=001, 107=1110, 108=1100, 111=1011}
//4.压缩后的字节数组
byte[] bt2 = compress(bt1, encodingTable);
/*
110100011001101111011110001100110111101111000110011011110111100000101101001
1000100010110001001101110100001100110111101111000011000001011010011
*/
System.out.println(Arrays.toString(bt2) + "---" + bt2.length);
//[-47, -101, -34, 51, 123, -58, 111, 120, 45, 49, 22, 38, -24, 102, -9, -122, 11, 76]---18
/**
* 解压
*/
String res = deCompress(bt2);
System.out.println(res);
}
//1.获取Node节点的集合
public static ArrayList<Node> getNodes(byte[] bt) {
ArrayList<Node> nodes = new ArrayList<>();
//统计每个字符出现的次数,存在HashMap中
HashMap<Byte, Integer> map = new HashMap<>();
int i = 0;
for (byte date : bt) {
if (map.get(date) == null) {
map.put(date, i);
} else {
i++;
map.put(date, i);
}
}
//遍历HashMap,创建Node对象,并添加到ArrayList中
Set<Byte> set = map.keySet();
for (Byte b : set) {
Node node = new Node(b, map.get(b));
nodes.add(node);
}
return nodes;
}
//2.创建哈夫曼树
public static Node createHuffmanTree(ArrayList<Node> nodes) {
//将Nodes按照权重排序
while (nodes.size() > 1) {
Collections.sort(nodes, new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
return o1.weight - o2.weight;
}
});
//取出最小的两个节点
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集合
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
}
return nodes.get(0);
}
//3.获取编码表
static HashMap<Byte, String> encodingTable = new HashMap<>();
static StringBuilder sb = new StringBuilder();
public static HashMap<Byte, String> getCode(Node root) {
if (root != null) {
getCode(root, "", sb);
}
return encodingTable;
}
public static void getCode(Node node, String code, StringBuilder sb) {
StringBuilder cur = new StringBuilder(sb);
cur.append(code);
if (node.date == null) {
getCode(node.left, "0", cur);
getCode(node.right, "1", cur);
} else {
encodingTable.put(node.date, cur.toString());
}
}
//4.根据编码表,压缩字节数组
public static byte[] compress(byte[] bt1, HashMap<Byte, String> encodingTable) {
StringBuilder sb = new StringBuilder();
for (byte b : bt1) {
String s = encodingTable.get(b);
sb.append(s);
}
System.out.println(sb.toString());
//将字符串的长度补全为8的整数倍
if (sb.length() % 8 != 0) {
int n = 8 - sb.length() % 8;
for (int i = 0; i < n; i++) {
sb.append("0");
}
}
//字符串转字节数组
byte[] res = new byte[sb.length() / 8];
int index = 0;
for (int i = 0; i < sb.length(); i += 8) {
String substring = sb.substring(i, i + 8);
byte b = (byte) Integer.parseInt(substring, 2);//二进制转十进制
res[index] = b;
index++;
}
return res;
}
//5.解压
public static String deCompress(byte[] bt2) {
//十进制转二进制
StringBuilder sb = new StringBuilder();
for (byte b : bt2) {
int temp = b;
temp |= 256;//或运算,只要有一个为1就为1,用于补高位
String str = Integer.toBinaryString(temp);
str = str.substring(str.length() - 8);
sb.append(str);
}
//反转编码表
HashMap<String, Byte> reverseEncodingTable = new HashMap<>();
Set<Byte> set = encodingTable.keySet();
for (Byte b : set) {
String s = encodingTable.get(b);
reverseEncodingTable.put(s, b);
}
//用编码表匹配
ArrayList<Byte> arrayList = new ArrayList();
int i = 0;
int count = 1;
while (i + count < sb.length()) {//!!!要考虑最后补零的情况
String temp = sb.substring(i, i + count);
if (reverseEncodingTable.containsKey(temp)) {
arrayList.add(reverseEncodingTable.get(temp));
i += count;
count = 1;
} else {
count++;
}
}
//遍历arrayList
StringBuilder res = new StringBuilder();
for (int x = 0; x < arrayList.size(); x++) {
byte temp = arrayList.get(x);
res.append((char) temp);
}
return res.toString();
}
}