关于对哈夫曼树解码的最后一个字节的处理

由韩顺平老师的网课代码,其对哈夫曼树解码的最后一个字节可能会出现bug

在此提出解决方法,在压缩过程中多开辟一个空间用于存储最后一个字节的长度

这是全部代码

class HuffmanCode {
    //哈夫曼编码对应map
    private static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
    //stringBuilder用于拼接路径
    private static StringBuilder stringBuilder = new StringBuilder();

    public static List<Node> getNodes(String s) {
        return getNodes(s.getBytes());
    }

    //根据数据生成一个列表
    private static List<Node> getNodes(byte[] bytes) {
        List<Node> list = new ArrayList<>();
        Map<Byte, Integer> map = new HashMap<>();
        for (int i = 0; i < bytes.length; i++) {
            if (map.get(bytes[i]) == null) {
                map.put(bytes[i], 1);
            } else {
                map.put(bytes[i], map.get(bytes[i]) + 1);
            }
        }
        for (Map.Entry<Byte, Integer> byteIntegerEntry : map.entrySet()) {
            list.add(new Node(byteIntegerEntry.getKey(), byteIntegerEntry.getValue()));
        }
        return list;
    }

    //根据列表生成一个哈夫曼树
    public static Node createHuffmanTree(List<Node> list) {
        while (list.size() > 1) {
            Collections.sort(list);
            Node node1 = list.remove(0);
            Node node2 = list.remove(0);
            Node node = new Node(null, node1.weight + node2.weight);
            node.left = node1;
            node.right = node2;
            list.add(node);
        }
        return list.remove(0);
    }

    //获取编码
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        stringBuilder1.append(code);
        if (node != null) {
            if (node.data == null) {
                getCodes(node.left, "0", stringBuilder1);
                getCodes(node.right, "1", stringBuilder1);
            } else {
                huffmanCodes.put(node.data, stringBuilder1.toString());
            }
        }
    }

    //重载一下
    public static Map getCodes(Node root) {
        if (root == null) {
            return null;
        }
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.right, "1", stringBuilder);
        return huffmanCodes;
    }

    //获取编码表【最终】
    public static Map<Byte, String> get(String s) {
        return getCodes(createHuffmanTree(getNodes(s)));
    }

    //压缩
    public static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        StringBuilder stringBuilder1 = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            stringBuilder1.append(huffmanCodes.get(bytes[i]));
        }
        int len;
        if (stringBuilder1.length() % 8 == 0) {
            len = stringBuilder1.length() / 8;
        } else {
            len = stringBuilder1.length() / 8 + 1;
        }
        byte[] huffmanCodeBytes = new byte[len + 1];
        int index = 0;
        int length = 0;
        for (int i = 0; i < stringBuilder1.length(); i += 8) {
            String str;
            if (i + 8 > stringBuilder1.length()) {
                str = stringBuilder1.substring(i);
                //huffmanCodeBytes最后一个空间存储 最后一个字符串的长度

                length = str.length();
            } else {
                str = stringBuilder1.substring(i, i + 8);
            }
            huffmanCodeBytes[index] = (byte) Integer.parseInt(str, 2);
            index++;
        }
        huffmanCodeBytes[index] = (byte) length;
        //倒数第二个一定为正数,因为其的位数最多为七位
        return huffmanCodeBytes;
    }

    //重载一下
    public static byte[] zip(String s) {
        return zip(s.getBytes(), huffmanCodes);
    }

    //解压
    //生成对应的字符串
    public static String byteToBitString(boolean flag, byte b) {
        int tmp = b;
        if (flag) {
            tmp |= 256;
        }
        String str = Integer.toBinaryString(tmp);
        return str.substring(str.length() - 8);
    }

    //正式解压
    public static byte[] decode(byte[] huffmanCodeBytes) {
        boolean flag = false;
        StringBuilder stringBuilder1 = new StringBuilder();
        for (int i = 0; i < huffmanCodeBytes.length - 1; i++) {
            if (i == huffmanCodeBytes.length - 2) {
                String string = new String();
                string += Integer.toBinaryString(huffmanCodeBytes[i]);
                while ((int) huffmanCodeBytes[i + 1] > string.length()) {
                    string = "0" + string;
                }
                stringBuilder1.append(string);
            } else {
                if (huffmanCodeBytes[i] >= 0) {
                    stringBuilder1.append(byteToBitString(true, huffmanCodeBytes[i]));
                } else {
                    stringBuilder1.append(byteToBitString(false, huffmanCodeBytes[i]));
                }
            }
        }
        //把huffmanCodes倒置
        Map<String, Byte> map = new HashMap<>();
        for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
            map.put(entry.getValue(), entry.getKey());
        }

        List list = new ArrayList();
        Byte b = null;
        int count;
        boolean loop = true;
        String key;
        int i = 0;
        while (i < stringBuilder1.length()) {
            count = 1;
            loop = true;
            while (loop) {
                key = stringBuilder1.substring(i, i + count);
                b = map.get(key);
                if (b == null) {
                    count++;
                } else {
                    loop = false;
                }
            }
            list.add(b);
            i = i + count;
        }

        byte[] bytes = new byte[list.size()];
        for (int j = 0; j < bytes.length; j++) {
            bytes[j] = (Byte) list.get(j);
        }
        return bytes;
    }
}

class Node implements Comparable {
    Byte data;
    int weight;
    Node left;
    Node right;


    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    @Override
    public int compareTo(Object o) {
        return this.weight - ((Node) o).weight;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是C++实现的哈夫曼树对文件进行压缩和加密的代码: ```cpp #include <iostream> #include <fstream> #include <queue> #include <bitset> #include <vector> using namespace std; // 定义哈夫曼树结点 struct TreeNode { char c; // 字符 int freq; // 频率 TreeNode* left; // 左儿子 TreeNode* right; // 右儿子 TreeNode(char c, int freq, TreeNode* left, TreeNode* right) : c(c), freq(freq), left(left), right(right) {} }; // 定义优先队列比较函数 struct cmp { bool operator()(TreeNode* a, TreeNode* b) { return a->freq > b->freq; } }; // 生成哈夫曼树 TreeNode* generateHuffmanTree(const string& s) { // 统计字符频率 int freq[256] = { 0 }; for (char c : s) { freq[c]++; } // 将结点放入优先队列 priority_queue<TreeNode*, vector<TreeNode*>, cmp> pq; for (int i = 0; i < 256; i++) { if (freq[i] > 0) { pq.push(new TreeNode(i, freq[i], nullptr, nullptr)); } } // 构建哈夫曼树 while (pq.size() > 1) { TreeNode* left = pq.top(); pq.pop(); TreeNode* right = pq.top(); pq.pop(); TreeNode* parent = new TreeNode('\0', left->freq + right->freq, left, right); pq.push(parent); } return pq.top(); } // 生成字符编码表 void generateEncodingTable(TreeNode* root, vector<string>& encodingTable, string code) { if (root == nullptr) { return; } if (root->left == nullptr && root->right == nullptr) { encodingTable[root->c] = code; return; } generateEncodingTable(root->left, encodingTable, code + "0"); generateEncodingTable(root->right, encodingTable, code + "1"); } // 压缩文件 void compressFile(const string& inputFile, const string& outputFile, vector<string>& encodingTable) { // 读入文件 ifstream ifs(inputFile, ios::binary); string s((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>()); ifs.close(); // 将字符编码转为二进制字符串 string binaryStr = ""; for (char c : s) { binaryStr += encodingTable[c]; } // 补齐 8 的倍数 int padding = 8 - binaryStr.size() % 8; for (int i = 0; i < padding; i++) { binaryStr += "0"; } // 将二进制字符串转为字节流 vector<char> bytes; for (int i = 0; i < binaryStr.size(); i += 8) { bytes.push_back(bitset<8>(binaryStr.substr(i, 8)).to_ulong()); } // 写入文件 ofstream ofs(outputFile, ios::binary); ofs.write(&padding, sizeof(padding)); int size = encodingTable.size(); ofs.write((char*)&size, sizeof(size)); for (int i = 0; i < encodingTable.size(); i++) { ofs.write(&i, sizeof(char)); int len = encodingTable[i].size(); ofs.write((char*)&len, sizeof(len)); ofs.write(encodingTable[i].c_str(), len); } ofs.write(bytes.data(), bytes.size()); ofs.close(); } // 解压文件 void decompressFile(const string& inputFile, const string& outputFile) { // 读入文件 ifstream ifs(inputFile, ios::binary); int padding; ifs.read((char*)&padding, sizeof(padding)); int size; ifs.read((char*)&size, sizeof(size)); vector<string> encodingTable(256); for (int i = 0; i < size; i++) { char c; ifs.read(&c, sizeof(char)); int len; ifs.read((char*)&len, sizeof(len)); char* buf = new char[len + 1]; ifs.read(buf, len); buf[len] = '\0'; encodingTable[c] = buf; delete[] buf; } string s((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>()); ifs.close(); // 将字节流转为二进制字符串 string binaryStr = ""; for (char c : s) { binaryStr += bitset<8>(c).to_string(); } binaryStr = binaryStr.substr(0, binaryStr.size() - padding); // 生成反向字符编码表 vector<char> decodingTable(256, -1); for (int i = 0; i < encodingTable.size(); i++) { if (!encodingTable[i].empty()) { decodingTable[bitset<8>(encodingTable[i]).to_ulong()] = i; } } // 解码 string decodedStr = ""; int i = 0; while (i < binaryStr.size()) { int j = i; while (j < binaryStr.size() && decodingTable[bitset<8>(binaryStr.substr(i, j - i + 1)).to_ulong()] == -1) { j++; } decodedStr += decodingTable[bitset<8>(binaryStr.substr(i, j - i + 1)).to_ulong()]; i = j + 1; } // 写入文件 ofstream ofs(outputFile, ios::binary); ofs.write(decodedStr.c_str(), decodedStr.size()); ofs.close(); } int main() { string inputFile = "input.txt"; // 输入文件名 string compressedFile = "compressed.bin"; // 压缩后的文件名 string decompressedFile = "decompressed.txt"; // 解压后的文件名 // 读入文件 ifstream ifs(inputFile); string s((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>()); ifs.close(); // 生成哈夫曼树和编码表 TreeNode* root = generateHuffmanTree(s); vector<string> encodingTable(256); generateEncodingTable(root, encodingTable, ""); // 压缩文件 compressFile(inputFile, compressedFile, encodingTable); // 解压文件 decompressFile(compressedFile, decompressedFile); return 0; } ``` 以上代码的流程如下: 1. 读入输入文件并统计字符频率。 2. 生成哈夫曼树。 3. 生成字符编码表。 4. 将输入文件的字符编码转为二进制字符串,补齐 8 的倍数,并将二进制字符串转为字节流。 5. 将字符编码表、补齐的位数和字节流写入输出文件。 6. 读入压缩文件并解析字符编码表、补齐的位数和字节流。 7. 将字节流转为二进制字符串,解码并写入输出文件。 注意:以上代码仅供参考,实际使用时还需要考虑错误处理、内存泄漏等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值