<ol><li class="alt"><span><span class="comment">1、建立哈夫曼树</span> </span></li><li><span><span class="comment">2、从每个叶结点回溯到root的路径,并记录路径,则为哈夫曼编码</span> </span></li><li class="alt"><span><span class="comment">3、查表方式获得每个字符的哈夫曼编码</span> </span></li></ol>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
///----------------------定义结点数据---------------
#define leave 26
#define node (26*2-1)
typedef struct nodee
{
char character;
float weight;
int lson;
int rson;
int par;
}Node,*pnode;
typedef struct code
{
int hufcode[leave];///叶节点最长编码位数应该为树的最长路径 ,储存编码结果
int sta; ///编码起始位(相对编码数组)
char Char; ///编码的字符值
}Code,*pcode;
///----------------------构造哈夫曼树----------------
void huffman(Node ht[],float wt[])
{
int i,j,x1,x2;
float min1,min2;
///初始化结点数组ht ,即对huffman树进行初始化
for(i=0;i<node;i++)
{
ht[i].par=-1;
ht[i].lson=-1;
ht[i].rson=-1;
if(i<leave)
{
ht[i].weight=wt[i];
ht[i].character=i+65;///A-Z的ASCii码
}
else
{
ht[i].weight=0;
ht[i].character='?';///生成的中间结点字符值标记为'?'
}
}
///控制n-1次结点的结合(若有n个叶结点)
for(i=1;i<leave;i++)
{
min1=min2=100;///min1、min2记录当前最小、次小权值
x1=x2=0; ///x1、x2记录当前最小次小权值结点的位置(数组标号)
for(j=0;j<leave-1+i;j++) ///在[0-j]范围内找最小次小权值结点
{
if(ht[j].par==-1 && ht[j].weight<min1)///parent元素的判断是为了排除已结合过的结点,结合过的结点parent有正值
{
min2=min1;///当前结点权值小于最小值,所以当前结点变成最小权值结点,原最小结点变成原来的次小结点
x2=x1;
min1=ht[j].weight;
x1=j;
}
else
{
if(ht[j].par==-1 && ht[j].weight<min2) ///当前结点权值大于最小值,小于次小值,则取代次小结点
{
min2=ht[j].weight;
x2=j;
}
}
}
///将找到的最小、次小权值结点结合成树,为其父结点赋值,可见该哈夫曼树的根节点应该是ht数组最后一个结点ht[node-1]
ht[x1].par=leave-1+i;
ht[x2].par=leave-1+i;
ht[leave-1+i].weight=ht[x1].weight+ht[x2].weight;
ht[leave-1+i].lson=x1;
ht[leave-1+i].rson=x2;
}
}
///--------------------获取并保存每个叶节点的哈夫曼编码供解码时查询--------------------------
void codeht(Node ht[],Code hc[])
{
int i,j,d,p;
Code x;
///依次每个叶结点(在哈夫曼结点数组的最前面的空间中)寻找双亲直到root,记录路径,路径就是哈夫曼编码
for(i=0;i<leave;i++)///从每个字母都向上找寻根节点,使得整个树完整的编码
{
x.Char=ht[i].character;
x.sta=leave-1;///默认编码起点为编码数组最后一位
d=i;
p=ht[i].par;
while(1)
{
if(ht[p].lson == d)
x.hufcode[x.sta]=0;///默认编码为左0右1
else if(ht[p].rson==d)
x.hufcode[x.sta]=1;
else
printf("ERROR!!");
d=p;
p=ht[d].par;///继续向着根延伸编码
if(p==-1)break;///到了26个字母以外停止,ht[i]为root结点退出循环,说明已经回溯到了根结点
x.sta--;
}
for(j=x.sta;j<leave;j++)
{
hc[i].hufcode[j]=x.hufcode[j];
}
hc[i].sta=x.sta;
hc[i].Char=x.Char;
}
}
///--------------------输出每个字符的的哈夫曼编码------------------------
void printcode(Code hc[])
{
int i,j;
for(i=0;i<leave;i++)
{
printf("字母%c的huffman编码为:",hc[i].Char);
for(j=hc[i].sta;j<leave;j++)
{
printf("%d",hc[i].hufcode[j]);
}
printf("\n");
}
}
///-----------------------查询字符的编码---------------------------
void findcode(Code hc[])
{
int i,j;
char x;
printf("请输入一个大写英文字母:");
while(scanf("%c",&x)!=EOF)
{
getchar();
for(i=0;i<leave;i++)
{
if(x==hc[i].Char)
{
printf("字符%c的huffman编码为:",x);
for(j=hc[i].sta;j<leave;j++)
{
printf("%d",hc[i].hufcode[j]);
}
printf("\n");
}
}
printf("请输入一个大写英文字母:");
}
}
///---------------------主函数-----------------------
int main()
{
Node huftree[node];///存放所有结点数据
Code hcode[leave];///存放所有的编码结果
///存放叶结点权值
float wt[leave]={0.0856,0.0139,0.0297,0.0378,0.1304,0.0289,0.0199,0.0528,0.0627,0.0013,0.0042,0.0339,0.0249,0.0707,0.0797,0.0199,0.0012,0.0677,0.0607,0.1045,0.0249,0.0092,0.0149,0.0017,0.0199,0.0008};
huffman(huftree,wt);
codeht(huftree,hcode);
printcode(hcode);///调用可以输出所有的字母编码
findcode(hcode);
return 0;
}