数据压缩过程称为编码,即将文件中的每个字符均转换为一个唯一的二进制位串。
编码方案通常有两种:等长和变长。如字符集C={a, b, c},等长编码{00, 01, 10},变长编码{0, 10, 11}。变长编码方案将出现频度高的字符编码设置成较短编码,将频度低的字符编码设置为较长编码,可有效节约存储空间。
前缀码是指,字符集中任一字符的编码都不是其他字符编码前缀的编码。如上例中变长编码。平均码长或文件总长最小的前缀编码称为最优前缀码。前缀码中具有典型的二叉树结构。
Huffman Tree指n个叶节点,权分别为 w 1 , w 2 , w 3 , . . . , w n w_1, w_2, w_3,...,w_n w1,w2,w3,...,wn 的二叉树中,带权路径长度WPL最小的二叉树,即最优二叉树。以分支权重代表出现频率,自底向上每次取最小的权,其根的权值为左右子树根的权值的和。如此重复,最终合并为一最优二叉树。该树叶子节点为字符,从根节点到叶子节点的路径(左0右1)即为该字符最优编码。
要求实现Huffman编码。
测试用例:
C={b,c,j,m,p},F={5,6,2,9,7},编码:b:101; c:00; j:100 m:11; p:01
题目要求明确,数据结构也已给出。c++STL中并没有封装树(虽然set和map是用红黑树实现的),所以需要自己定义并实现。获得最优编码的常见思路是构建最优二叉树之后,遍历得到编码。同时我们只能选择自底向上建立二叉树,所以得到的编码必然也是逆序,需要反转之后再存入节点中。
#include
#include
#include
#include
using namespace std;
struct Node{
string ch, code;
int lchild, rchild, parent, w;
};
void FindTwoSmallestNode(Node hufftree[], int &t1, int &t2, int k){
int sw1, sw2, flag = 0;
for(int i = 0; i < k; ++i){//满足条件的节点一定在k左边
if(hufftree[i].parent == -1){//节点未被选择过
if(flag == 0){
sw1 = hufftree[i].w;
t1 = i;
++flag;
continue;
}
if(flag == 1){
sw2 = hufftree[i].w;
t2 = i;
++flag;
continue;
}
if(hufftree[i].w != -1){//节点权值已初始化
if(hufftree[i].w < sw1){
sw2 = sw1;
t2 = t1;
sw1 = hufftree[i].w;
t1 = i;
}
else if(hufftree[i].w < sw2){
sw2 = hufftree[i].w;
t2 = i;
}
}
}
}
if(sw1 > sw2){
int tmp = t1;
t1 = t2;
t2 = tmp;
}
}
void Huffman_Tree(Node hufftree[], string chs[], int ws[], int n){
for(int i = 0; i < 2 * n - 1; ++i){//初始化树
hufftree[i].parent = -1;
hufftree[i].lchild = -1;
hufftree[i].rchild = -1;
hufftree[i].ch = "";
}
for(int i = 0; i < n; ++i){//初始化叶子结点
hufftree[i].ch = chs[i];
hufftree[i].w = ws[i];
}
for(int i = n; i < 2 * n - 1; ++i){//形成树
int t1 = 0, t2 = 0;
FindTwoSmallestNode(hufftree, t1, t2, i);//将t1节点和t2节点合成为i节点
hufftree[i].w = hufftree[t1].w + hufftree[t2].w;
hufftree[t1].parent = i;
hufftree[t2].parent = i;
hufftree[i].lchild = t1;
hufftree[i].rchild = t2;
}
}
void Huffman_Code(Node hufftree[], int n){
string s;
for(int i = 0; i < n; ++i){
s = "";
int cur = i, p;
while(hufftree[cur].parent != -1){//未到根节点
p = hufftree[cur].parent;
if(cur == hufftree[p].lchild){
s += "0";
}
else{
s += "1";
}
cur = p;
}
reverse(s.begin(), s.end());
hufftree[i].code = s;
cout<<setw(15)<<hufftree[i].ch<<setw(15)<<s<<endl;
}
}
int main(){
const int n = 5;
Node huffmanTree[2 * n];//前n为叶子结点
string chs[] = {"b", "c", "j", "m", "p"};
int ws[] = {5, 6, 2, 9, 7};
Huffman_Tree(huffmanTree, chs, ws, n);
cout<<setw(15)<<"character"<<setw(15)<<"Huffman_Code"<<endl;
Huffman_Code(huffmanTree, n);
return 0;
}