赫夫曼树(最优二叉树 )

赫夫曼树

数学概念: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;
	}
}


如果没有批评,赞美将毫无意义,欢迎指正批评。

路漫漫其修远兮,吾将上下而求索

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值