哈夫曼编码的压缩、解码

一、使用哈夫曼编码对字节数组进行压缩

1、创建哈夫曼树

节点

class NodeNo implements Comparable<NodeNo>{
	Byte data; //存放数据
	int weight; //权值
	NodeNo left; //左子节点
	NodeNo right; //右子节点
	
	public NodeNo(Byte data, int weight) {
		this.data = data;
		this.weight = weight;
	}

	@Override
	public int compareTo(NodeNo o) {
		return this.weight - o.weight;
	}
	
	//前序遍历
	public void pre(NodeNo node) {
		if(node==null) {
			return;
		}
		System.out.print(node.weight + " ");
		pre(node.left);
		pre(node.right);
	}
}
//创建哈夫曼树
public static NodeNo create(byte[] bytes) {

	//创建一个列表
	List<NodeNo> list = new ArrayList<NodeNo>();
	
	//用于记录每一个节点的权值
	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);
		}
	}
	
	//将数据存放到列表中
	Set<Byte> set = counts.keySet();
	for(Byte b : set) {
		list.add(new NodeNo(b, counts.get(b)));
	}
	
	//创建哈夫曼树
	while(list.size() > 1) { //遍历列表
		//对列表进行排序
		Collections.sort(list);
		//取出前两个值
		NodeNo temp1 = list.get(0);
		NodeNo temp2 = list.get(1);
		
		//将两个数进行相加得到父节点
		NodeNo parent = new NodeNo(null, temp1.weight + temp2.weight);
		parent.left = temp1;
		parent.right = temp2;
		
		//从列表中删除最小的两个节点
		list.remove(temp1);
		list.remove(temp2);
		
		//将新节点加入到列表中
		list.add(parent);
	}
	return list.get(0);
}

2、获取每个叶子节点的哈夫曼编码

//获取每个叶子节点的哈夫曼编码
public static Map<Byte, String> getHaffmanCodes(NodeNo node, String code) {

	if(node.left==null && node.right==null) {
		map.put(node.data, code);
		return map;
	}
	if(node.left!=null) {
		getHaffmanCodes(node.left, code + "0");
	}
	if(node.right!=null) {
		getHaffmanCodes(node.right, code + "1");
	}
	return map;
}

3、获取压缩之后的字节数组

//获取压缩之后的字节数组
public static byte[] getHaffmanCodeBytes(byte bytes[]) {

	//字符串,将编码拼串
	String str = new String();
	for(Byte b : bytes) { //从哈夫曼编码中获取二进制编码
		str += map.get(b);
	}
	//System.out.println(str);
	//每个字节占8为
	//获取字节数组的长度
	int len = (str.length() + 7) / 8;
	byte[] haffmanByts = new byte[len];
	int j = 0;
	for(int i=0; i<str.length(); i+=8) { //每8位占一个字节
		String temp;
		if(i+8 < str.length()) { //截取字符串
			temp = str.substring(i, i+8);
		} else {
			temp = str.substring(i);
		}
		haffmanByts[j++] = (byte)Integer.parseInt(temp, 2); 
	}
	
	return haffmanByts;
}

4、整合压缩过程

//生成哈夫曼编码
static Map<Byte, String> map = null;

//编码
public static byte[] getCodes(byte[] bytes) {
	//重新初始化map集合
	map = null;
	map = new HashMap<>();
	NodeNo node = create(bytes); //创建哈夫曼树
	getHaffmanCodes(node, ""); //获取哈夫曼编码
	return getHaffmanCodeBytes(bytes); //得到压缩的字节数组
}

二、解码

1、将字节转化位一个二进制字符串

//将字节转化位一个二进制字符串
//flag用来标记是否需要补高位,如果需要补高位,值为true,如果是最后一个字节,无需不高位
public static String byteToBitString(boolean flag, byte b) {
	int temp = b;
	if(flag) {
		//补高位,按位或
		temp |= 256;
	}
	//返回temp的二进制补码
	String str = Integer.toBinaryString(temp);
	
	if(flag) {
		str = str.substring(str.length() - 8);
	}
	
	return str;
}

2、对压缩的字节数组进行解码得到原始的字节数组

//对压缩的数据进行解码
	public static byte[] decode(byte[] haffmanByte) {
		
		String bitStr = "";
		//利用字节数组得到二进制字符串
		for(int i=0; i<haffmanByte.length; i++) {
			//判断是不是最后一个字节,获取二进制补码
			bitStr += byteToBitString(!(i==haffmanByte.length-1), haffmanByte[i]);
		}
		
		//将二进制编码表进行倒转
		Map<String, Byte> codeToString = new HashMap<>();
		for(Map.Entry<Byte, String> entry: map.entrySet()) {
			codeToString.put(entry.getValue(), entry.getKey());
		}
		
		//将解码之后的字符二进制编码存放在列表中
		List<Byte> list = new ArrayList<>();
		for(int i=0; i<bitStr.length();) {
			int count = 1;
			boolean flag =true;
			Byte b = null;
			while(flag) {
				//字符串匹配
				String temp = bitStr.substring(i, i+count);
				b = codeToString.get(temp);
				if(b!=null) {
					flag = false;
				} else {
					count++;
				}
			}
			list.add(b);
			i += count;
		}
		byte[] b = new byte[list.size()];
		int i = 0;
		for(Byte t : list) {
			b[i++] = t;
		}
		//String s = new String(b);
		//System.out.println(s.length());
		return b;
	}

三、文件压缩

//对文件进行压缩
public static void zipFile(String file, String toSave) {
	FileInputStream is = null; 
	OutputStream os = null;
	ObjectOutputStream oos = null;
	try {
		//初始化三个文件流
		is = new FileInputStream(file);
		
		//读取文件的字节数组
		byte[] b = new byte[is.available()];
		is.read(b);
		//使用哈夫曼编码进行压缩
		byte[] res = getCodes(b);
		os = new FileOutputStream(toSave);
		oos = new ObjectOutputStream(os);
		//将编码后的字节数组以及哈夫曼编码写入压缩文件中
		oos.writeObject(res);
		oos.writeObject(map);
	} catch(Exception e) {
		System.out.println("文件压缩出错");
	} finally {
		//关闭各种流
		try {
			is.close();
			oos.close();
			os.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

四、文件解压

//解压文件
public static void unZipFile(String zipFile, String dstFile) {
	//定义文件输入流
	InputStream is = null;
	//定义一个对象输入流
	ObjectInputStream ois = null;
	//定义文件的输出流
	OutputStream os = null;
	try {
		//创建文件输入流
		is = new FileInputStream(zipFile);
		//创建一个和 is 关联的对象输入流
		ois = new ObjectInputStream(is);
		//读取 byte 数组 huffmanBytes
		byte[] huffmanBytes = (byte[])ois.readObject();
		//读取赫夫曼编码表
		map = (Map<Byte,String>)ois.readObject();
		//解码
		byte[] bytes = decode(huffmanBytes);
		//将 bytes 数组写入到目标文件
		os = new FileOutputStream(dstFile);
		//写数据到 dstFile 文件
		os.write(bytes);
	} catch (Exception e) {
		// TODO: handle exception
		System.out.println(e.getMessage());
	} finally {
		try {
			os.close();
			ois.close();
			is.close();
		} catch (Exception e2) {
			// TODO: handle exception
			System.out.println(e2.getMessage());
		}
	}
}

五、全部代码

public class HaffmanNo {
	public static void main(String[] args) {
		byte[] b = "i like like like java do you like a java".getBytes();
		//输出压缩前大小
//		System.out.println(b.length);
		//压缩后
//		NodeNo no = NodeHaffmanNo.create(b); 
//		no.pre(no);
//		System.out.println("---------------------------------------------------------------------------------------");
//		System.out.println(NodeHaffmanNo.getHaffmanCodes(no, ""));
//		System.out.println("---------------------------------------------------------------------------------------");
//		byte[] b1 = NodeHaffmanNo.getHaffmanCodeBytes(b);
//		System.out.println(Arrays.toString(b1));
//		System.out.println("解码后------------------------------------------------------------");
//		System.out.println(NodeHaffmanNo.decode(NodeHaffmanNo.map, b1));
//		byte[] codes = NodeHaffmanNo.getCodes(b);
//		System.out.println(Arrays.toString(codes));
//		System.out.println(new String(NodeHaffmanNo.decode(codes)));
		
		NodeHaffmanNo.zipFile("C:\\Users\\10507\\Desktop\\实验用图\\1.jpg", "D:\\b.zip");
		//NodeHaffmanNo.unZipFile("D:\\a.zip", "D:\\1.jpg");
		System.out.println("压缩完成");
	}
}

class NodeHaffmanNo {
	//生成哈夫曼编码
	static Map<Byte, String> map = null;
	
	//编码
	public static byte[] getCodes(byte[] bytes) {
		//重新初始化map集合
		map = null;
		map = new HashMap<>();
		NodeNo node = create(bytes); //创建哈夫曼树
		getHaffmanCodes(node, ""); //获取哈夫曼编码
		return getHaffmanCodeBytes(bytes); //得到压缩的字节数组
	}
	
	//创建哈夫曼树
	public static NodeNo create(byte[] bytes) {

		//创建一个列表
		List<NodeNo> list = new ArrayList<NodeNo>();
		
		//用于记录每一个节点的权值
		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);
			}
		}
		
		//将数据存放到列表中
		Set<Byte> set = counts.keySet();
		for(Byte b : set) {
			list.add(new NodeNo(b, counts.get(b)));
		}
		
		//创建哈夫曼树
		while(list.size() > 1) { //遍历列表
			//对列表进行排序
			Collections.sort(list);
			//取出前两个值
			NodeNo temp1 = list.get(0);
			NodeNo temp2 = list.get(1);
			
			//将两个数进行相加得到父节点
			NodeNo parent = new NodeNo(null, temp1.weight + temp2.weight);
			parent.left = temp1;
			parent.right = temp2;
			
			//从列表中删除最小的两个节点
			list.remove(temp1);
			list.remove(temp2);
			
			//将新节点加入到列表中
			list.add(parent);
		}
		return list.get(0);
		
	}
	
	//获取每个叶子节点的哈夫曼编码
	public static Map<Byte, String> getHaffmanCodes(NodeNo node, String code) {

		if(node.left==null && node.right==null) {
			map.put(node.data, code);
			return map;
		}
		if(node.left!=null) {
			getHaffmanCodes(node.left, code + "0");
		}
		if(node.right!=null) {
			getHaffmanCodes(node.right, code + "1");
		}
		return map;
	}
	
	//获取压缩之后的字节数组
	public static byte[] getHaffmanCodeBytes(byte bytes[]) {

		//字符串,将编码拼串
		String str = new String();
		for(Byte b : bytes) { //从哈夫曼编码中获取二进制编码
			str += map.get(b);
		}
		//System.out.println(str);
		//每个字节占8为
		//获取字节数组的长度
		int len = (str.length() + 7) / 8;
		byte[] haffmanByts = new byte[len];
		int j = 0;
		for(int i=0; i<str.length(); i+=8) { //每8位占一个字节
			String temp;
			if(i+8 < str.length()) { //截取字符串
				temp = str.substring(i, i+8);
			} else {
				temp = str.substring(i);
			}
			haffmanByts[j++] = (byte)Integer.parseInt(temp, 2); 
		}
		
		return haffmanByts;
	}
	
	//将字节转化位一个二进制字符串
	//flag用来标记是否需要补高位,如果需要补高位,值为true,如果是最后一个字节,无需不高位
	public static String byteToBitString(boolean flag, byte b) {
		int temp = b;
		if(flag) {
			//补高位,按位或
			temp |= 256;
		}
		//返回temp的二进制补码
		String str = Integer.toBinaryString(temp);
		
		if(flag) {
			str = str.substring(str.length() - 8);
		}
		
		return str;
	}
	
	//对压缩的数据进行解码
	public static byte[] decode(byte[] haffmanByte) {
		
		String bitStr = "";
		//利用字节数组得到二进制字符串
		for(int i=0; i<haffmanByte.length; i++) {
			//判断是不是最后一个字节,获取二进制补码
			bitStr += byteToBitString(!(i==haffmanByte.length-1), haffmanByte[i]);
		}
		
		//将二进制编码表进行倒转
		Map<String, Byte> codeToString = new HashMap<>();
		for(Map.Entry<Byte, String> entry: map.entrySet()) {
			codeToString.put(entry.getValue(), entry.getKey());
		}
		
		//将解码之后的字符二进制编码存放在列表中
		List<Byte> list = new ArrayList<>();
		for(int i=0; i<bitStr.length();) {
			int count = 1;
			boolean flag =true;
			Byte b = null;
			while(flag) {
				//字符串匹配
				String temp = bitStr.substring(i, i+count);
				b = codeToString.get(temp);
				if(b!=null) {
					flag = false;
				} else {
					count++;
				}
			}
			list.add(b);
			i += count;
		}
		byte[] b = new byte[list.size()];
		int i = 0;
		for(Byte t : list) {
			b[i++] = t;
		}
		//String s = new String(b);
		//System.out.println(s.length());
		return b;
	}
	
	//对文件进行压缩
	public static void zipFile(String file, String toSave) {
		FileInputStream is = null; 
		OutputStream os = null;
		ObjectOutputStream oos = null;
		try {
			//初始化三个文件流
			is = new FileInputStream(file);
			
			//读取文件的字节数组
			byte[] b = new byte[is.available()];
			is.read(b);
			//使用哈夫曼编码进行压缩
			byte[] res = getCodes(b);
			os = new FileOutputStream(toSave);
			oos = new ObjectOutputStream(os);
			//将编码后的字节数组以及哈夫曼编码写入压缩文件中
			oos.writeObject(res);
			oos.writeObject(map);
		} catch(Exception e) {
			System.out.println("文件压缩出错");
		} finally {
			//关闭各种流
			try {
				is.close();
				oos.close();
				os.close();
				
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	//解压文件
	public static void unZipFile(String zipFile, String dstFile) {
		//定义文件输入流
		InputStream is = null;
		//定义一个对象输入流
		ObjectInputStream ois = null;
		//定义文件的输出流
		OutputStream os = null;
		try {
			//创建文件输入流
			is = new FileInputStream(zipFile);
			//创建一个和 is 关联的对象输入流
			ois = new ObjectInputStream(is);
			//读取 byte 数组 huffmanBytes
			byte[] huffmanBytes = (byte[])ois.readObject();
			//读取赫夫曼编码表
			map = (Map<Byte,String>)ois.readObject();
			//解码
			byte[] bytes = decode(huffmanBytes);
			//将 bytes 数组写入到目标文件
			os = new FileOutputStream(dstFile);
			//写数据到 dstFile 文件
			os.write(bytes);
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println(e.getMessage());
		} finally {
			try {
				os.close();
				ois.close();
				is.close();
			} catch (Exception e2) {
				// TODO: handle exception
				System.out.println(e2.getMessage());
			}
		}
	}

}

class NodeNo implements Comparable<NodeNo>{
	Byte data; //存放数据
	int weight; //权值
	NodeNo left; //左子节点
	NodeNo right; //右子节点
	
	public NodeNo(Byte data, int weight) {
		this.data = data;
		this.weight = weight;
	}

	@Override
	public int compareTo(NodeNo o) {
		return this.weight - o.weight;
	}
	
	//前序遍历
	public void pre(NodeNo node) {
		if(node==null) {
			return;
		}
		System.out.print(node.weight + " ");
		pre(node.left);
		pre(node.right);
	}
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值