伴学笔记:详解哈夫曼编码实现,让小白也能看懂!🌟
哈夫曼编码是一种常用的数据压缩算法,它通过为使用频率高的字符分配更短的编码来减少总数据量。本篇伴学笔记将逐步拆解上述代码,帮助你理解哈夫曼编码的核心思想及其实现方式。
哈夫曼编码的核心思想 🧠
- 统计字符频率
找出每个字符出现的次数,这决定了它的编码长度。出现频率高的字符分配短编码,频率低的分配长编码。 - 构建哈夫曼树
根据字符的频率构建一棵二叉树。频率低的字符作为叶子节点,频率高的字符更接近根节点。 - 生成编码表
从树根到每个叶子节点的路径生成二进制编码:左子树为0,右子树为1。 - 编码与解码
使用编码表将字符串转化为二进制串,或将二进制串解码回原字符串。
代码拆解:一步步理解哈夫曼编码实现 ✨
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) {}
};
每个节点存储一个字符和它的频率。树通过 left 和 right 指针链接。
2. 构建哈夫曼树
步骤:
- 统计频率: 用
unordered_map记录每个字符出现的次数。 - 优先队列: 使用
priority_queue按频率从小到大排列节点。 - 合并节点: 每次从队列中取出两个权重最小的节点,生成新节点作为它们的父节点。
- 完成树: 当队列中只剩下一个节点时,整棵哈夫曼树就构建完成。
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
优点与不足 📝
优点:
- 节省存储空间。
- 无损压缩,解码后可恢复原始数据。
不足:
- 构建树和生成编码表需要额外时间。
- 对动态或实时数据不适用。
总结
通过这份笔记,大家是不是对哈夫曼编码有了更清晰的理解呢?✨
如果有疑问,欢迎随时提问,学习路上不孤单哟!(*≧ω≦)💕