halo各位看客们,最近接触到了一个很有趣且神奇的东西:哈夫曼编码!!!
那么什么是哈夫曼编码呢?我们以前看抗日神剧的时候可能经常接触到神秘电报密码这种东西,没错了,很多电报的破译就是由哈夫曼编码构成的,话不多说,让我们来见识一下这个神秘的东西。
先上我们在开头的宏定义以及头文件:
#include
#include
#include
#define MaxLeaf 10000
#define MaxBit 100
#define MaxValue 10000
#define MaxNode MaxLeaf*2 - 1
typedef struct {
double weight;
int parent;
int lchild;
int rchild;
char value;
}HuffNodeT;
typedef struct {
int bit[MaxBit];
int start;
}HuffNodeC;
HuffNodeT HuffNode[MaxNode];
HuffNodeC HuffCode[MaxLeaf];
在英文中有26个字母,在我们中文里也有五笔的25个一级简码,那么假如我们得到了一些字母以及他的使用频率,我们如何去得到它们的哈夫曼编码呢?首先我们需要得到一颗哈夫曼树:
void CreateHuffNode(HuffNodeT HuffNode[MaxNode],int n) {
int i,j;
int x1,x2;
double m1,m2;
for(i = 0;i < 2*n-1;i++) {
HuffNode[i].lchild = -1;
HuffNode[i].rchild = -1;
HuffNode[i].parent = -1;
HuffNode[i].weight = 0;
}
for(i = 0; i < n;i++) {
scanf(" %c %lf",&HuffNode[i].value,&HuffNode[i].weight);
//printf("2");
}
for(i = 0;i < n-1;i++) {
m1 = m2 = MaxValue;
x1 = x2 = 0;
for (j = 0 ;j < n+i;j++) {
if(HuffNode[j].weight < m1 && HuffNode[j].parent == -1) {
m2 = m1;
x2 = x1;
m1 = HuffNode[j].weight;
x1 = j;
}
else if (HuffNode[j].weight < m2 && HuffNode[j].parent == -1) {
m2 = HuffNode[j].weight;
x2 = j;
}
}
HuffNode[x1].parent = n+i;
HuffNode[x2].parent = n+i;
HuffNode[n+i].weight = m1 +m2;
HuffNode[n+i].lchild = x1;
HuffNode[n+i].rchild = x2;
//printf("2");
}
}
(注意!!在scanf时%c前要加空格 不然会导致输入错误)
我们使用结构体的方式使每一个除初始节点的节点有他的左右儿子以及父亲。
因为我们有N个节点,所以我们需要2*N - 1个节点空间来构建这颗树。
首先进行N - 2次循环,每次循环找到没有在树中的两个和最小的节点加入树,由此我们便得到了一颗哈夫曼树。
接下来就是编码哈夫曼树:
void CreateHuffCode(HuffNodeC HuffCode[MaxLeaf],int n) {
HuffNodeC temp;
int i,j;
int c,p;
for(i = 0;i < n;i++) {
temp.start = n-1;
c = i;
p = HuffNode[c].parent;
while(p != -1) {
if(HuffNode[p].lchild == c) {
temp.bit[temp.start] = 0;
}
else {
temp.bit[temp.start] = 1;
}
temp.start--;
c = p;
p = HuffNode[c].parent;
}
for(j = temp.start;j < n;j++) {
HuffCode[i].bit[j] = temp.bit[j];
HuffCode[i].start = temp.start;
}
}
}
对这一课哈夫曼树,我们令每个分支上左儿子为0,右儿子为1,并将每一个节点的编码储存在编码数组中。
储存方式用到的逆向储存,通过start保存输出停止的位置。
最后是主程序,以及输出哈夫曼编码:
int main () {
int i,j,n;
scanf("%d",&n);
CreateHuffNode(HuffNode,n);
CreateHuffCode(HuffCode,n);
for(i = 0; i < n;i++) {
printf("%c:",HuffNode[i].value);
for(j = HuffCode[i].start + 1;j < n;j++) {
printf("%d",HuffCode[i].bit[j]);
}
printf("\n");
}
return 0;
}
对此,我们输入数据测试结果:
可以看到,对于频率高的字符,得到的编码越简单,对于频率低的字符,得到的编码就越复杂。同时这也是比较符合我们的生活习惯的使用方式。