
问题引入
继上章,我们学会了哈夫曼编码的编写,那么我们如何将哈夫曼编码转会原来的字符串?,这章我们将进入介绍。
哈夫曼解码的编写
总述
1.将哈夫曼编码重新写为哈夫曼编码对应的二进制字符串
2.将哈夫曼编码对应的二进制字符串对照哈夫曼编码恢复原字符串
步骤一
首先我们需要将哈夫曼编码转变成哈夫曼编码对应的二进制字符串。
由于每个哈夫曼编码都是一个字节(最后一个可能不是一个字节,上章在步骤四有介绍),所有我们需要将字节转换为字节转化为一个二进制的字符串,当所有哈夫曼编码的字节对应的二进制编码连接在一起,就是我们需要的二进制字符串。
以下的decode方法还是一个雏形,只是将哈夫曼编码转变成哈夫曼编码对应的二进制字符串,执行完decode后stringBuilder里面放着的即是我们需要的二进制字符串。
//将一个byte 转为一个二进制的字符串
private static String byteToBitString(boolean flag,byte b){
//使用变量保存b
int temp = b;
//如果是正整数还存在部高位的问题
if(flag){
temp |= 256; //按位与 1 0000 0000 | 0000 0001 => 1 0000 0001
}
String str = Integer.toBinaryString(temp); //返回的是temp对应的二进制的补码
if(flag){
return str.substring(str.length() - 8);
}else {
return str;
}
}
//获得哈夫曼编码对应的二进制字符串
private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
//1.先得到 huffmanBytes 对应的二进制的字符串,形式101010000...
StringBuilder stringBuilder = new StringBuilder();
//将byte数组转为二进制的字符串
for (int i = 0; i < huffmanBytes.length; i++) {
//判断是否为最后一个字节
boolean flag = (i == huffmanBytes.length - 1);
stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));
}
}
步骤二
将哈夫曼编码对应的二进制字符串对照哈夫曼编码恢复原字符串。
接下来我们继续完善decode方法。以上的decode有两个参数,一个是Map<Byte,String> huffmanCodes,即我们编写的哈夫曼编码表,另一个是byte[] huffmanBytes,即存放哈夫曼编码的字节数组。
我们将huffmanCodes编码表进行调换,因为我们需要反向编码,即用字符串中的数字来对应字符串中的字符。
最后我们将所得到的字符串放入list集合中,所有我们创建一个List list = new ArrayList<>();,存放byte.
接下来我们需要遍历stringBuilder得到对应的字符串,将得到的每一个字符放入list中,然后将list中的每个字符再放入字符数组byte[] b = new byte[list.size()]中,然后返回。
完整的decode方法如下:
//将哈夫曼编码解码
private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
//1.先得到 huffmanBytes 对应的二进制的字符串,形式101010000...
StringBuilder stringBuilder = new StringBuilder();
//将byte数组转为二进制的字符串
for (int i = 0; i < huffmanBytes.length; i++) {
//判断是否为最后一个字节
boolean flag = (i == huffmanBytes.length - 1);
stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));
}
//把字符串按照指定的赫夫曼编码进行解码
//把赫夫曼编码表进行调换,因为反向查询 a->100 100->a
Map<String,Byte> map = new HashMap<String,Byte>();
for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
map.put(entry.getValue(),entry.getKey());
}
//创建一个集合,存放byte
List<Byte> list = new ArrayList<>();
for (int i = 0; i < stringBuilder.length();) {
int count = 1; //小的计数器
boolean flag = true;
Byte b = null;
while (flag){
String key = stringBuilder.substring(i,i + count);
b = map.get(key);
if(b == null){
count++;
}else {
flag = false;
}
}
list.add(b);
i += count;
}
//当for循环结束后,我们从list中就存放所有的字符 "i like like like java do you like a java"
//把list中的数据放入byte[] 并返回
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
完整代码如下
public class HuffmanCode {
public static void main(String[] args) {
//以下为将指定字符串用赫夫曼编码转为字节数组的过程,并将该字节数组转为原字符串
String str = "i like like like java do you like a java";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
byte[] huffmanCodeBytes = huffmanZip(bytes);
System.out.println("压缩后的结果为 : " + Arrays.toString(huffmanCodeBytes));
//上述方法在上章已经编写完成,需要大家copy一下到此文件中,不然会报错.
//下面是已经编写好的方法。
byte[] sourceBytes = decode(buffmanCodes, huffmanCodeBytes);
System.out.println("原来的字符串: " + new String(sourceBytes));
}
//将哈夫曼编码解码
private static byte[] decode(Map<Byte,String> huffmanCodes, byte[] huffmanBytes){
//1.先得到 huffmanBytes 对应的二进制的字符串,形式101010000...
StringBuilder stringBuilder = new StringBuilder();
//将byte数组转为二进制的字符串
for (int i = 0; i < huffmanBytes.length; i++) {
//判断是否为最后一个字节
boolean flag = (i == huffmanBytes.length - 1);
stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));
}
//把字符串按照指定的赫夫曼编码进行解码
//把赫夫曼编码表进行调换,因为反向查询 a->100 100->a
Map<String,Byte> map = new HashMap<String,Byte>();
for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
map.put(entry.getValue(),entry.getKey());
}
//创建一个集合,存放byte
List<Byte> list = new ArrayList<>();
for (int i = 0; i < stringBuilder.length();) {
int count = 1; //小的计数器
boolean flag = true;
Byte b = null;
while (flag){
String key = stringBuilder.substring(i,i + count);
b = map.get(key);
if(b == null){
count++;
}else {
flag = false;
}
}
list.add(b);
i += count;
}
//当for循环结束后,我们从list中就存放所有的字符 "i like like like java do you like a java"
//把list中的数据放入byte[] 并返回
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
//将一个byte 转为一个二进制的字符串
private static String byteToBitString(boolean flag,byte b){
//使用变量保存b
int temp = b;
//如果是正整数还存在部高位的问题
if(flag){
temp |= 256; //按位与 1 0000 0000 | 0000 0001 => 1 0000 0001
}
String str = Integer.toBinaryString(temp); //返回的是temp对应的二进制的补码
if(flag){
return str.substring(str.length() - 8);
}else {
return str;
}
}
}