哈夫曼编码
使用变长编码表对字符进行编码,出现频率高的字符采用较短的编码,出现频率低的采用较长的编码。以达到编码后的字符串的平均长度尽可能短,以达到无损压缩数据的目的。
图解
有一字符串string word = "abbbcccccdddddddd"
,字符a
,b
,c
,d
权重分别为1,3,5,8。
初始
第一次合并
第二次合并
第三次合并
字符 权重 编码
d 8 0
a 1 100
b 3 101
c 5 11
模板
按照字符串各字符的出现频率建立哈夫曼树,则每一个叶子结点对应一个字符。记每个结点的左分支为0,右分支为1,得到的编码即为哈夫曼编码。
/*
结构中左右结点未设置为空,导致在先序遍历时进入空结点输出权值报错!
*/
#include <iostream>
#include <map>
#include <queue>
using namespace std;
struct TreeNode
{
int weight;
char ch = ' ';
string code;
// 要设为空,不然在遍历时会出错
TreeNode *left = NULL;
TreeNode *right = NULL;
};
//自定义排序规则
class mycomparison
{
public:
bool operator()(const TreeNode *a, const TreeNode *b)
{
return a->weight > b->weight;
}
};
// 构建哈夫曼树
TreeNode *Huffman(priority_queue<TreeNode *, vector<TreeNode *>, mycomparison> *H)
{
TreeNode *T;
int times = H->size();
// 做times-1次合并
for (int i = 1; i < times; i++)
{
T = new TreeNode;
T->left = H->top();
H->pop();
T->right = H->top();
H->pop();
T->weight = T->left->weight + T->right->weight;
H->push(T);
}
// T = H->top();
// H->pop();
return H->top();
}
// 改写先序遍历,输出叶子结点
void PreOrderTraversal(TreeNode *Huff)
{
if (Huff)
{
if (Huff->ch != ' ')
{
cout << Huff->ch << " " << Huff->weight << " " << Huff->code << endl;
}
PreOrderTraversal(Huff->left);
PreOrderTraversal(Huff->right);
}
}
// 哈夫曼树编码
void GetCode(TreeNode *Huff, string code)
{
if (Huff)
{
Huff->code = code;
GetCode(Huff->left, code + "0");
GetCode(Huff->right, code + "1");
}
}
int main()
{
string word = "abbbcccccdddddddd";
map<char, int> Hash;
for (auto ch : word)
{
if (!Hash.count(ch))
{
Hash.emplace(ch, 1);
}
else
{
Hash[ch]++;
}
}
// 删除空格
Hash.erase(' ');
// 建堆
priority_queue<TreeNode *, vector<TreeNode *>, mycomparison> MinHeap;
int count = 0;
for (auto i : Hash)
{
TreeNode *T = new TreeNode;
T->weight = i.second;
T->ch = i.first;
MinHeap.push(T);
}
TreeNode *Tree = Huffman(&MinHeap);
GetCode(Tree, "");
cout << "Char Weight Code" << endl;
PreOrderTraversal(Tree);
system("pause");
return 0;
}