C语言 数据结构实验五 哈夫曼树的创建与哈夫曼编码
实验内容:
假设用于通信的电文仅由a,b,c,d,e,f,g,h几个字母组成,字母在电文中出现的频率分别为0.07 0.19 0.02 0.06 0.32 0.03 0.21 0.10,试为这些字母设计哈夫曼编码。
设计思想
哈夫曼树构造:每次把根结点权值最小的两个二叉树合并,新结点的权值等于两个根结点权值之和,得到新的二叉树。
结构体构造:包括数据、权值、以及父亲、左孩、右孩结点在数组中位置。
哈夫曼编码:规定哈夫曼树中全部左分支表示字符0,全部右分支表示字符1,将依次从根结点到每一个叶子结点所经过的分支的二进制位的序列作为该结点相应的字符编码。
代码如下:
哈夫曼树和编码结构体
#define N 100
typedef struct
/*静态三叉链的扩展存储,包括数据+权值+父亲、左孩、右孩结点在数组中位置*/
{
char data; /*结点值*/
double weight; /*权重*/
int parent; /*双亲结点*/
int lchild; /*左孩子结点*/
int rchild; /*右孩子结点*/
} HTNode;
typedef struct
{
char cd[N]; //存放当前结点的哈夫曼码
int start; //表示cd[start…n0]部分是哈夫曼码
}HCode;
创建哈夫曼树
void createHT(HTNode ht[],int n0)
{
int i,k,lnode,rnode;
double min1,min2;
for(i=0;i<2*n0-1;i++) //所有结点的相关域值初值为-1
ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
for(i=n0;i<=2*n0-2;i++) //构造哈夫曼树的n0-1个分支结点
{
min1=min2=32767; //lnode和rnode为最小权重的两个结点位置
lnode=rnode=-1;
for(k=0;k<=i-1;k++) //在ht[0…i-1]中找权值最小的两个结点
if(ht[k].parent==-1) //只在尚未构造二叉树的结点中查找
{
if(ht[k].weight<min1)
{
min2=min1;rnode=lnode;
min1=ht[k].weight;lnode=k;
}
else if(ht[k].weight<min2)
{
min2=ht[k].weight; rnode=k;
}
}
ht[i].weight=ht[lnode].weight+ht[rnode].weight;
ht[i].lchild=lnode; ht[i].rchild=rnode; //ht[i]作为双亲结点
ht[lnode].parent=i; ht[rnode].parent=i;
}
}
哈夫曼编码
void createHCode(HTNode ht[],HCode hcd[],int n0,int L[])
{
int i,f,c;
HCode hc;
for(i=0;i<n0;i++) //根据哈夫曼树求哈夫曼编码
{
hc.start=n0;c=i;
f=ht[i].parent;
while(f!=-1) //循环直到无双亲结点,即达到根结点
{
if(ht[f].lchild==c) //当前结点是双亲结点的左孩子
hc.cd[hc.start--]='0';
else //当前结点是双亲结点的右孩子
hc.cd[hc.start--]='1';
c=f;f=ht[f].parent; //再对双亲结点进行同样的操作
}
hc.start++; //start指向哈夫曼编码最开始的字符
hcd[i]=hc;
L[i]=n0-hc.start+1;
}
}
主函数
int main()
{
//输入相应的值,构造哈夫曼树
int n0;
printf("请输入编码字符个数:");
scanf("%d",&n0);
HTNode ht[N];
printf("请输入待编码字符:");
getchar();
for(int i=0;i<n0;i++)
{
scanf("%c",&ht[i].data);
}
getchar();
printf("请输入相应的权值:");
for( int i=0;i<n0;i++)
{
scanf("%lf",&ht[i].weight);
}
createHT( ht, n0);
//对字符进行哈夫曼编码并输出
int L[n0];
HCode hcd[n0];
createHCode(ht, hcd, n0,L);
printf("\n输出哈夫曼编码:\n");
for(int i=0;i<n0;i++)
{
printf("%c:\t",ht[i].data);
for(int j=hcd[i].start;j<=n0;j++) //输出哈夫曼码
{
printf("%c",hcd[i].cd[j]);
}
printf("\t length:%d\n",L[i]);
}
double WPL; //WAP表示带权路径长度
for (int i;i<n0;i++)
{
WPL += ht[i].weight*L[i];
}
printf("WPL:%lf",WPL);
}
运行如下
另外注意一点,我把权值定义了double型,但之前scanf输入时使用的确是%f,结果出现了乱码,后来改成了%lf。
看了下别人的博客,因为float和double的关系不像int和long的关系那样,简单的在后面增加4字节的位置。float和double有自己专门的数据排列格式,如下:
附上链接