尚硅谷赫夫曼解码

韩顺平又又又讲错了

把赫夫曼编码得到的字节数组转换为每个元素共同拼接成的二进制字符串的方法需要修改,修改后的代码如下:

private static int count;
    private static String byteToBitString(boolean flag, byte b) { //flag处理最后一个char不够8位的问题
        //使用变量保存b
        int temp = b; //将b转成int
        //如果是正数,还存在补高位的问题
        if (flag) {
            temp |= 256; //按位或256  1 0000 0000 | 0000 0001 => 1 0000 0001
        }

        String str = Integer.toBinaryString(temp); //返回temp对应的二进制补码(int的补码,负数有32位,正数就是那么多位,但是要补位)
        if (flag) {
            return str.substring(str.length() - 8);
        } else {
            int length = count - str.length();
            for (int i = 0; i < length; i++) {
                str = 0 + str;
            }
            System.out.println(str);
            if (str.length() > 8) {
                return str.substring(str.length() - 8);
            } else {
                return str;
            }
        }
    }

关于Integer.toBinaryString()方法,具体可以这样理解:

  1. 当传入的数是一个正数的时候,返回的是正数本身的二进制补码。由于正数源码、反码、补码三码合一,所以对于正数而言,需要进行的操作只有一个,就是把位数补满8位,因为一个字节等于8个比特。

  2. 当传入的是一个负数的时候,返回的是一个int类的补码,一共有32位。需要做的工作就是截取后八位。

1和2可以合并通过 temp |= 256 操作去将正数补满8位,而负数经过这个操作并不会受影响。

处理的重点应该是对于最后一个字节的数据的处理。最后一个字节是有非常大可能在编码时不足8位的,比如0010,这个代表十进制的2,有对应相应的ascii码的信息,但是在解码的时候,2会被解码成10,就会缺失一开始的00。解决这种问题的方法就是要在编码的时候记录一下最后一个字节储存的二进制的位数,代码如下:

private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        count = 0;
        //1. 利用huffmanCodes将bytes转成赫夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        //遍历bytes
        for (byte b : bytes) {
            stringBuilder.append(huffmanCodes.get(b));
        }

        System.out.println(stringBuilder);

        //将10101000....转成byte[]
        //统计返回byte[] huffmanCodeBytes长度
        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }

        //创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0; //记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i += 8) { //每8位对应一个byte,所以步长为8
            String strByte;
            if (i + 8 >= stringBuilder.length()) {
                strByte = stringBuilder.substring(i);
                for (int j = i; j < stringBuilder.length(); j++) {
                    count++;
                }
            } else {
                strByte = stringBuilder.substring(i, i + 8);
            }

            //将strByte转成一个byte,放入到huffmanCodeBytes
            huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte, 2);
            index++;
        }

        return huffmanCodeBytes;


    }

需要对编码的zip方法进行修改,增加代码:

for (int j = i; j < stringBuilder.length(); j++) {
                    count++;
}

这段代码记录了编码时最后一个二进制字节的位数。到时候,如果最后一位面临0010这种情况,解码出来以后是10,前面有几个零就补几个0。

另外如果最后一位二进制字节恰满8位,也就是代表一个负数,也需要单独考虑。负数会返回一个32位的字符串,判断一下如果最后那个字符串大于8,直接截取后8位就好了。

posted @ 2021-05-07 15:56  imissinstagram  Views(139)  Comments(0)    收藏  举报