在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
例如,在英文中,e的出现机率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节,即8个比特。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。
霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明霍夫曼树的WPL是最小的
#include <iostream>
using namespace std;
char N[] = { 'A','B','C','D','E','F','G' };
int E[] = { 12,22,34,56,66,78,99 };
struct H_code
{
char name;
int weight;
int Lchild;
int Rchild;
int father;
};
struct H_code H[13];
int main()
{
int i, j, t;
int m1, m2, k1, k2;
for (i = 0;i < 13;i++)
H[i].Lchild = H[i].Rchild = H[i].father = -1;
for (i = 0;i < 7;i++)
{
H[i].name = N[i];
H[i].weight = E[i];
}
for (i = 0;i < 6;i++)
{
m1 = m2 = 1000;
k1 = k2 = -1;
for (j = 0;j < (7 + i);j++)
{
if (H[j].father == -1) {
if (H[j].weight < m1)
{
m2 = m1;k2 = k1;
m1 = H[j].weight;
k1 = j;
}
else
if (H[j].weight < m2)
{
m2 = H[j].weight;
k2 = j;
}
}
}
//cout<<k1<<'-'<<m1<<k2<<'-'<<m2<<endl;
H[7 + i].weight = H[k1].weight + H[k2].weight;
H[7 + i].Lchild = k1;
H[7 + i].Rchild = k2;
H[k1].father = H[k2].father = 7 + i;
}
//这里是用编码的思想来推出树的结构,是左孩子就是0,反之是1
for (i = 0;i < 7;i++)
{
cout << H[i].name << ':' << H[i].weight << ' ';
t = i;
while (H[t].father != -1)
{
if (t == H[H[t].father].Lchild)
cout << '0';
else cout << '1';
t = H[t].father;
}
cout << endl;
}
return 0;
}