伴学笔记:详解哈夫曼编码实现 | 豆包MarsCode AI刷题

152 阅读3分钟

伴学笔记:详解哈夫曼编码实现,让小白也能看懂!🌟

哈夫曼编码是一种常用的数据压缩算法,它通过为使用频率高的字符分配更短的编码来减少总数据量。本篇伴学笔记将逐步拆解上述代码,帮助你理解哈夫曼编码的核心思想及其实现方式。


哈夫曼编码的核心思想 🧠

  1. 统计字符频率
    找出每个字符出现的次数,这决定了它的编码长度。出现频率高的字符分配短编码,频率低的分配长编码。
  2. 构建哈夫曼树
    根据字符的频率构建一棵二叉树。频率低的字符作为叶子节点,频率高的字符更接近根节点。
  3. 生成编码表
    从树根到每个叶子节点的路径生成二进制编码:左子树为 0,右子树为 1
  4. 编码与解码
    使用编码表将字符串转化为二进制串,或将二进制串解码回原字符串。

代码拆解:一步步理解哈夫曼编码实现 ✨

1. 定义节点结构

class HuffmanNode {
public:	
	char ch;       // 字符
	int weight;    // 字符频率
	HuffmanNode* left;   // 左子树
	HuffmanNode* right;  // 右子树

	HuffmanNode(char c, int w) : ch(c), weight(w), left(nullptr), right(nullptr) {}
};

每个节点存储一个字符和它的频率。树通过 leftright 指针链接。


2. 构建哈夫曼树

步骤:

  1. 统计频率:unordered_map 记录每个字符出现的次数。
  2. 优先队列: 使用 priority_queue 按频率从小到大排列节点。
  3. 合并节点: 每次从队列中取出两个权重最小的节点,生成新节点作为它们的父节点。
  4. 完成树: 当队列中只剩下一个节点时,整棵哈夫曼树就构建完成。
void createTree(const string& text) {
    unordered_map<char, int> weightMap;
    for (char ch : text) {
        weightMap[ch]++;
    }

    priority_queue<HuffmanNode*, vector<HuffmanNode*>, compare> minHeap;
    for (const auto& pair : weightMap) {
        minHeap.push(new HuffmanNode(pair.first, pair.second));
    }

    while (minHeap.size() > 1) {
        HuffmanNode* left = minHeap.top(); minHeap.pop();
        HuffmanNode* right = minHeap.top(); minHeap.pop();
        HuffmanNode* parent = new HuffmanNode('\0', left->weight + right->weight);
        parent->left = left;
        parent->right = right;
        minHeap.push(parent);
    }
    root = minHeap.top();
    createTable(root, "");  // 创建编码表
}

3. 生成哈夫曼编码表

递归遍历哈夫曼树,从根到叶子的路径生成编码。路径上的方向(左 0,右 1)构成每个字符的二进制编码。

void createTable(HuffmanNode* node, const string& code) {
    if (!node) return;
    if (!node->left && !node->right) {  // 叶子节点
        charToCode[node->ch] = code;
        codeToChar[code] = node->ch;
    }
    createTable(node->left, code + '0');
    createTable(node->right, code + '1');
}

4. 编码与解码

编码: 将字符串中每个字符替换为对应的哈夫曼编码。

string encode(const string& text) {
    string encoded = "";
    for (char ch : text) {
        encoded += charToCode[ch];
    }
    return encoded;
}

解码: 通过哈夫曼树逐位还原二进制串。

string decode(const string& encoded) {
    string decoded = "";
    HuffmanNode* currentNode = root;
    for (char bit : encoded) {
        currentNode = (bit == '0') ? currentNode->left : currentNode->right;
        if (currentNode && !currentNode->left && !currentNode->right) {
            decoded += currentNode->ch;
            currentNode = root;
        }
    }
    return decoded;
}

运行结果分析 🌟

输入: i love you

  • 构建的哈夫曼表可能如下:

    i: 000
    l: 001
    o: 01
    v: 100
    e: 101
    y: 110
    u: 111
    
  • 编码结果: 000001011001011101110

  • 解码结果: i love you


优点与不足 📝

优点:

  • 节省存储空间。
  • 无损压缩,解码后可恢复原始数据。

不足:

  • 构建树和生成编码表需要额外时间。
  • 对动态或实时数据不适用。

总结

通过这份笔记,大家是不是对哈夫曼编码有了更清晰的理解呢?✨
如果有疑问,欢迎随时提问,学习路上不孤单哟!(*≧ω≦)💕