赫夫曼树
数学概念:WPL 最短的树,WPL带权路径长度等于所有的节点的值*权值的合,
特点:每一个对应数据节点,的路径都不同,都是叶子节点
权:某个节点被赋予的值
路径:根节点到对应节点的长度
创建赫夫曼树的步骤:
代码实现:
package huffmantree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CreatehHuffMantTree {
public static void main(String[] args) {
int arr[] = { 13, 7, 8, 3, 29, 6, 1 };
point one = CREAThuffman(arr);
qianxubian(one);
}
//前序遍历
public static void qianxubian(point point) {
point.qianxubianli();
}
//创建赫夫曼树
public static point CREAThuffman(int arr[]) {
//创建一个集合老操控树
List<point> list = new ArrayList<point>();
// 将数组遍历除出 并逐个创建对应树 并加入集合中
for (int value : arr) {
list.add(new point(value));
}
//当集合长度为一时已经完成赫夫曼树的创建
while (list.size() > 1) {
//对集合中的数据进行排序
Collections.sort(list);
//获取到集合中的对应的第一小的第二小的数据 进行创建树 (升序还是降序 在节点类可以控制)
point point1 = list.get(0);
point point2 = list.get(1);
//两个数的权值相加形成一个新的权值数
point Newpoint = new point(point1.value + point2.value);
//并将新创建的树的左右分别加上子树 左边放小的 右边放大的
Newpoint.leftPoint = point1;
Newpoint.rightPoint = point2;
//移除两个子树
list.remove(point1);
list.remove(point2);
//添加新创建的子树
list.add(Newpoint);
}
//返回头节点
return list.get(0);
}
}
//创建赫夫曼树的节点类
class point implements Comparable<point> {
// 值
int value;
// 左子树
point leftPoint;
// 右子树
point rightPoint;
// 构造器初始化
public point(int value) {
this.value = value;
}
//前序遍历
public void qianxubianli() {
System.out.println(this);
if (this.leftPoint != null) {
this.leftPoint.qianxubianli();
}
if (this.rightPoint != null) {
this.rightPoint.qianxubianli();
}
}
//tostring
@Override
public String toString() {
return "point [value=" + value + "]";
}
//比较value大小
@Override
public int compareTo(point o) {
// TODO Auto-generated method stub
return this.value - o.value;
}
}
5.赫夫曼编码(压缩和解压)
数学概念:根据赫夫曼树获得一套对应的和赫夫曼编码表,使其压缩数据大小,关键在于利用赫夫曼树的特点,每个子数据都路径都不同 权值大的靠近根节点
编程思想:
代码实现`
package huffmanZip;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class huffmanzip {
public static void main(String[] args) {
/*
* 赫夫曼数据压缩(zip) 处理对象 :一段为String类型的数据 处理步骤: 1.将String 类型的数据加入到 byte数组中
* 2.将统计byte数组中的字母出现的次数 (将对应的 字母 字母出现次数 利用 MAP集合储存) 3.创建赫夫曼树节点类 规定对应的属性
* 4.将MAP集合中的数据对应创建 赫夫曼树节点 5.创建一个List 集合 数据类型为 赫夫曼树节点 来储存 这些对应的节点
* 6.利用创建赫夫曼树的方法创建对应的 赫夫曼树 (List集合中的数据) 7.根据路径记录赫夫曼编码 用另一个MAP集合储存 得一个对应得赫夫曼编码表
* 8.再将byte中得数据 利用赫夫曼编码表 获得对应得 赫夫曼编码 9.再将赫夫曼编码 转换成 byte(8位一个字符) 10.最终获得压缩之后得
* byte 数组
*/
/*
* 赫夫曼解压(UNzip):处理对象:一组byte[] 数组数据 ,将其解压成原有的String字符串 1.像将byte数组转换成 和赫夫曼编码
* (二进制字符串) 2.再对相应赫夫曼编码表,将赫夫曼编码转换成对应的string字符串
*/
String str = "i like like like java do you like a java";
// 将字符串转换位 二进制表示 并加入byte数组
byte[] by = str.getBytes();
//赫夫曼压缩
byte[] sr = Huffmanbianma(by);
//赫夫曼解压
byte[] xr = zhuanhuanString(huffmanCodes, sr);
//输出赫夫曼解压出的数据(既原数据)
System.out.println(new String(xr));
}
// 赫夫曼解压 unzip
// 将赫夫曼编码转换成对应的字符串
public static byte[] zhuanhuanString(Map<Byte, String> huffmancode, byte[] bt) {
// 将byte数组转换成对应的二进制字符串
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bt.length; i++) {
// 拼接字符串 得到最终的赫夫曼字符串
// 当到最后一个位置的时候直接返回 不要倒数最后
boolean flag = (i == bt.length - 1);
builder.append(zhuanhuanHuffman(!flag, bt[i]));
}
// 反转map集合
Map<String, Byte> map = new HashMap<>();
for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}
List<Byte> list = new ArrayList<Byte>();
for (int i = 0; i < builder.length();) {
int count = 1;
boolean b = true;
Byte by = null;
while (b) {
by = map.get(builder.substring(i, i + count));
if (by == null) {
count++;
} else {
b = false;
}
}
list.add(by);
i += count;
}
// 将集合中的数据返回到byte数组中
byte[] finaly = new byte[list.size()];
for (int i = 0; i < list.size(); i++) {
finaly[i] = list.get(i);
}
return finaly;
}
// 首先将byte 数组转换成 二进制字符串
public static String zhuanhuanHuffman(boolean flag, byte bt) {
// 将byte转化成int 一个int 4个字节 32位(bit) 如果没有32位自动复制补齐 不需要这么多只要8位
int temp = bt;
if (flag) {
// 按位或 256 补其最高位
temp |= 256;
}
String str = Integer.toBinaryString(temp);
//
if (flag) {
// 倒数八位开始
return str.substring(str.length() - 8);
} else {
// 当到最后的一个的时候直接return 全部 不用删除
return str;
}
}
//赫夫曼压缩 zip
// 将方法封装到一个总方法中
public static byte[] Huffmanbianma(byte[] by) {
// 统计string 字母(二进制)出现次数
List<point> list = stringchuli(by);
// 创建赫夫曼树
point p = createheffmanTree(list);
// 创建赫夫曼码表
Map<Byte, String> map = GETheffmancode(p);
// 压缩数据
byte[] sr = ZIP(by, map);
return sr;
}
// 返回一个对应的list集合 存储了对应的 数据 和数据量
public static List<point> stringchuli(byte[] by) {
List<point> list = new ArrayList<point>();
// 创建一个map数组要统计byte数组中字母数量和规格
Map<Byte, Integer> mapshuju = new HashMap<Byte, Integer>();
for (byte a : by) {
Integer count = mapshuju.get(a);
if (count == null) {
mapshuju.put(a, 1);
} else {
mapshuju.put(a, count + 1);
}
}
// map集合遍历方法
for (Map.Entry<Byte, Integer> entry : mapshuju.entrySet()) {
list.add(new point(entry.getValue(), entry.getKey()));
}
return list;
}
// 构建赫夫曼树
public static point createheffmanTree(List<point> list) {
while (list.size() > 1) {
// 升序排序
Collections.sort(list);
point left = list.get(0);
point right = list.get(1);
// 没用包装类 BYTE 所以不能用null 代替BYTE
point NEWpoint = new point(left.count + right.count);
NEWpoint.left = left;
NEWpoint.right = right;
list.remove(left);
list.remove(right);
list.add(NEWpoint);
}
return list.get(0);
}
// 储存赫夫曼码表的map 集合 将器放在方法外部
static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
// 2. 在生成赫夫曼编码表示,需要去拼接路径, 定义一个StringBuilder 存储某个叶子结点的路径
static StringBuilder stringBuilder = new StringBuilder();
// 重载生成码表的功能
public static Map<Byte, String> GETheffmancode(point root) {
if (root == null) {
return null;
}
// 左递归
GETheffmancode(root.left, "0", stringBuilder);
// 右递归
GETheffmancode(root.right, "1", stringBuilder);
return huffmanCodes;
}
// 获取赫夫曼编码表
public static void GETheffmancode(point point, String code, StringBuilder stringbuilder) {
// 字符串累加
StringBuilder builder = new StringBuilder(stringbuilder);
builder.append(code);
if (point != null) {
if (point.value == 0) {
// 左递归
GETheffmancode(point.left, "0", builder);
// 右递归
GETheffmancode(point.right, "1", builder);
} else {
// 当不等于零的时候 为叶子节点
huffmanCodes.put(point.value, builder.toString());
}
}
}
// 根据数据生成对应的赫夫曼码 压缩
public static byte[] ZIP(byte[] bt, Map<Byte, String> map) {
StringBuilder builder = new StringBuilder();
for (byte a : bt) {
builder.append(map.get(a));
}
int len;
if (builder.length() / 8 == 0) {
len = builder.length() / 8;
} else {
len = builder.length() / 8 + 1;
}
//创建对应的大小的byte数组
byte[] bs = new byte[len];
int index = 0;
for (int i = 0; i < builder.length(); i += 8) {
String str;
// 解决数组越界问题
if (i + 8 > builder.length()) {
str = builder.substring(i);
} else {
str = builder.substring(i, i + 8);
}
// 将字符串以八位分给再转为二进制byte
bs[index] = (byte) Integer.parseInt(str, 2);
index++;
}
return bs;
}
}
//创建赫夫曼编码节点
class point implements Comparable<point> {
// 数据量
int count;
// 二进制字节
byte value;
point left;
point right;
public point(int count, byte value) {
this.value = value;
this.count = count;
}
public point(int count) {
this.count = count;
}
public void qianxubianli() {
System.out.println(this);
if (this.left != null) {
this.left.qianxubianli();
}
if (this.right != null) {
this.right.qianxubianli();
}
}
@Override
public String toString() {
return "point [count=" + count + ", value=" + value + "]";
}
@Override
public int compareTo(point o) {
// TODO Auto-generated method stub
return this.count - o.count;
}
}
如果没有批评,赞美将毫无意义,欢迎指正批评。
路漫漫其修远兮,吾将上下而求索