<数据的解压> 哈夫曼编码的解压:
1. 将哈夫曼编码转换成哈夫曼编码对应的二进制字符串
2. 再将二进制字符串转换成原来的字符编码
补充知识点:
一、原码、反码、补码
黑马程序员讲解的非常清楚,有需要的可以去看看:
运算符-12-多学一招原码反码补码_哔哩哔哩_bilibili
原码:十进制的二进制的表现形式,最左边是符号位,0 为正 ,1 为负
(但若为负数计算,实际运算的方向是相反的。)
反码:< 为了解决负数的问题 >
正数的反码不变,负数的反码在原码的基础上,符号位不变,数值取反,0 变 1,1 变 0.
(负数运算时,若跨0计算,跟实际的结果会有1的偏差)
补码:< 为了解决负数计算时跨0的问题 >
正数的补码不变,负数的补码在反码的基础上 +1
(会多出来一个特殊值 -128,该数据在1个字节下,没有原码和反码)
< 计算机中的存储和计算都是以补码的形式进行的 >
< 正数的原码.反码.补码都一样,负数的补码在反码的基础上+1>
二、基本数据类型的强制转换
(8位为一个字节)
byte 类型的 10 :1 个字节 0000 1010
short 类型的10: 2 个字节 0000 0000 0000 1010
int 类型的10 : 4个字节 0000 0000 0000 0000 0000 0000 0000 1010
long 类型的10 : 8个字节 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 00001010
隐式转换:补0 eg: byte --> int
强制转换:去0 eg: int --> byte
逻辑与 & 0 为 false 1 为true ( 只有全为1,才为1)
逻辑或 | 0 为 false 1 为true ( 只要一个为1,就全为1)
一. 先将一个 byte 转换成一个二进制的字符串
涉及到二进制的转换问题:
(1) byte 类型的正数转换成二进制时,可能不足8位,通过 |= 256 强制第9位为1,生成9位字符串,再截取后8位.
(2) byte 类型的负数在转换成 int 时会以补码的形式拓展为32位, 而 |= 256 强制第9位为1,生成9位字符串,再截取后8位.
(3) 256 的二进制位 1 0000 0000,其第9位为1,其低位为0,按位或 |= 256 相当于将第9位设为1,确保生成的二进制字符串足够长,从而能够正确截取8位.
详细代码分析:
/**
* 将一个byte 转换成一个二进制的字符串
*
* @param flag 标志是否需要补高位,true:补高位,false:不用补位,如果是最后一个字节不用补高位
* @param b 传入的 byte 字节数组
* @return 是该 b 对应的二进制的字符串
*/
public static String byteToBinaryString(boolean flag, byte b) {
//将b字节 转换成int类型,先用一个变量保存b
int temp = b;
//如果是正数需要补高位
if (flag) {
//按位或 256 1 0000 0000 | 0000 0001 => 1 0000 0001
temp |= 256;
}
//因为 byte 类型没有提供直接转换成字符串的方法,所以只能通过转换成 int 类型来变成字符串
String str = Integer.toBinaryString(temp);
if (flag) {
return str.substring(str.length() - 8);
} else {
return str;
}
}
二. 解码的详细过程
/**
* 将二进制字符串解码成原来的字符串内容
* @param huffmanCodes 哈夫曼编码表 map
* @param huffmanBytes 哈夫曼编码得到的字节数组
* @return 原来的字符串对应的数组
*/
private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
// 先得到哈夫曼对应的二进制字符串
StringBuilder stringBuilder = new StringBuilder();
//将byte数组转换成二进制的字符串
for (int i = 0;i < huffmanBytes.length;i++){
byte b = huffmanBytes[i];
//判断是不是最后一个字节
boolean flag = (i == huffmanBytes.length - 1);
stringBuilder.append(byteToBinaryString(!flag,b));
}
//将字符串进行解码
//把哈夫曼编码表进行调换,反向查询 a -> 100 100 -> a
Map<String,Byte> map = new HashMap<>();
for (Map.Entry<Byte,String> entry:huffmanCodes.entrySet()){
map.put(entry.getValue(),entry.getKey());
}
System.out.println("map=" + map);
//创建集合,存放byte
List<Byte> list = new ArrayList<>();
//i相当于是索引,一个一个去扫描StringBuilder
for (int i = 0; i < stringBuilder.length();){
//定义一个变量记录扫描到的位置
int count = 1;
boolean flag = true;
Byte b = null;
while (flag){
//递增取出一个一个的key
String key = stringBuilder.substring(i,i+count);
b = map.get(key);
if (b == null){//没有匹配到
count++;
}else {//匹配到了
flag = false;
}
}
list.add(b);
i+= count;//i 移动到count每开始一个扫描的位置
}
//当for循环结束后list中存储了所有字符,再将list中的数据存入到byte[]数组
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}