一.具体任务
题目:哈夫曼树应用
功能:
1.从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树并将它存于文件hfmTree中.将已在内存中的哈夫曼树以直观的方式(比如树)显示在终端上;
2. 中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中,并输出结果。同时将此字符形式的编码文件写入文件CodePrint中。
3.利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中,并输出结果。
二.软件环境
Code::Blocks10.05
三.算法设计思想
1.建立哈夫曼树函数
void HuffTree(HNode Huff[],int n) //生成哈夫曼树
{
FILE *fp;
char d;
int i,j,w,m1,m2,x1,x2;
for(i=0;i<2*n-1;i++)
//对数组Huff初始化
{
Huff[i].ch=' ';
Huff[i].weight=0;
Huff[i].parent=-1;
Huff[i].lchild=-1;
Huff[i].rchild=-1;
}
printf("输入%d个字符及它的权值: \n",n);//读入数据
getchar();
for(i=0;i<n;i++)
{
printf("请输入第%d个字符:",i+1);
scanf("%c",&d);
getchar();
Huff[i].ch=d;
printf("请输入第%d个字符的权值:",i+1);
scanf("%d",&w);
getchar();
Huff[i].weight=w;
}
for(i=0;i<n-1;i++)
//构造哈夫曼树并生成该树的n-1个分支结点
{
m1=m2=32767;
x1=x2=0;
for(j=0;j<n+i;j++)
//选取最小和次小两个权值结点并将其序号送x1和x2
{
if(Huff[j].parent==-1&&Huff[j].weight<m1)
{
m2=m1;
x2=x1;
m1=Huff[j].weight;
x1=j;
}
else
if(Huff[j].parent==-1&&Huff[j].weight<m2)
{
m2=Huff[j].weight;
x2=j;
}
}
//将找出的两棵子树合并为一棵新的子树
Huff[x1].parent=n+i;
Huff[x2].parent=n+i;
Huff[n+i].weight=Huff[x1].weight+Huff[x2].weight;
Huff[n+i].lchild=x1;
Huff[n+i].rchild=x2;
}
2.对哈夫曼树进行编码
void HuffmanCode(HNode Huff[],int n)
//生成哈夫曼编码
{
FILE *fw;
HCode HuffCode[MAXSIZE/2],cd;
// MAXSIZE/2为叶结点的最大个数
int i,j,c,p;
for(i=0;i<n;i++)
//求每个叶结点的哈夫曼编码
{
HuffCode[i].weight=Huff[i].weight;
cd.start=MAXBIT-1;
c=i;
p=Huff[c].parent;
while(p!=-1)
{
if(Huff[p].lchild==c)
cd.bit[cd.start]=0;
else
cd.bit[cd.start]=1;
cd.start--;
c=p;
p=Huff[c].parent;
}
for(j=cd.start+1;j<MAXBIT;j++)
//保存该叶结点字符的哈夫曼编码
HuffCode[i].bit[j]=cd.bit[j];
HuffCode[i].start=cd.start;
//保存该编码在数组bit中的起始位置
}
3.根据哈夫曼编码进行译码
void decode(HNode Huff[],int n)
//依次读入电文,根据哈夫曼树译码
{
FILE *fs;
int i,j=0;
char b[MAXSIZE];
i=2*n-2;
//从根结点开始往下搜索
printf("【输入电文,并进行译码】\n");
getchar();
printf("输入发送的编码(以'2'为结束标志):\n");
gets(b);
printf("译码后的字符为:\n");
while(b[j]!='2')
{
if(b[j]=='0')
i=Huff[i].lchild; //走向左孩子
else
i=Huff[i].rchild; //走向右孩子
if(Huff[i].lchild==-1) //tree[i]是叶结点
{
printf("%c",Huff[i].ch);
i=2*n-2; //回到根结点
}
j++;
}
printf("\n");
if(Huff[i].lchild!=-1&&b[j]!='2')
//电文读完,但尚未到叶子结点
printf("\nERROR\n"); //输入电文
4.菜单调用
void menu()
{
printf("菜单如下\n");
printf("1-------建立哈夫曼树\n" );
printf("2-------进行哈夫编码\n");
printf("3-------进行哈夫译码\n");
printf("0-------程序退出\n");
}
5.main函数进行调用
int main()
{
HNode Huff[MAXSIZE];
int n,sel;
printf(" ——哈夫曼编码与译码——\n");
printf("Input numbers of leaf :\n");
//n为叶结点个数
scanf("%d",&n);
do
{
menu();
printf("请输入您的选择:\n");
scanf("%d",&sel);
switch(sel)
{
case 1:
HuffTree(Huff,n);//建立哈夫曼树
break;
case 2:
HuffmanCode(Huff,n); //生成哈夫曼编码
break;
case 3:
decode(Huff,n);//译码变代码
break;
}
}while(sel!=0);
return 0;
}
四.源代码
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 1000
#define MAXBIT 1000 //定义哈夫曼编码的最大长度
typedef struct
{
char ch;//增加一个域用于存放该节点的字符
int weight,parent,lchild,rchild;
}HNode; //哈夫曼树结点类型
typedef struct
{
int weight;
int bit[MAXBIT];
int start;
}HCode; //哈夫曼编码类型
void HuffTree(HNode Huff[],int n) //生成哈夫曼树
{
FILE *fp;
char d;
int i,j,w,m1,m2,x1,x2;
for(i=0;i<2*n-1;i++) //对数组Huff初始化
{
Huff[i].ch=' ';
Huff[i].weight=0;
Huff[i].parent=-1;
Huff[i].lchild=-1;
Huff[i].rchild=-1;
}
printf("输入%d个字符及它的权值: \n",n);//读入数据
getchar();
for(i=0;i<n;i++)
{
printf("请输入第%d个字符:",i+1);
scanf("%c",&d);
getchar();
Huff[i].ch=d;
printf("请输入第%d个字符的权值:",i+1);
scanf("%d",&w);
getchar();
Huff[i].weight=w;
}
for(i=0;i<n-1;i++)
//构造哈夫曼树并生成该树的n-1个分支结点
{
m1=m2=32767;
x1=x2=0;
for(j=0;j<n+i;j++)
//选取最小和次小两个权值结点并将其序号送x1和x2
{
if(Huff[j].parent==-1&&Huff[j].weight<m1)
{
m2=m1;
x2=x1;
m1=Huff[j].weight;
x1=j;
}
else
if(Huff[j].parent==-1&&Huff[j].weight<m2)
{
m2=Huff[j].weight;
x2=j;
}
}
//将找出的两棵子树合并为一棵新的子树
Huff[x1].parent=n+i;
Huff[x2].parent=n+i;
Huff[n+i].weight=Huff[x1].weight+Huff[x2].weight;
Huff[n+i].lchild=x1;
Huff[n+i].rchild=x2;
}
printf("哈夫曼树的列表:\n");
printf("\n_________________________________________________________|\n");
printf("zifu | Huff| weight | lchild | rchild| parent | \n");
for(i=0;i<2*n-1;i++)
//输出哈夫曼树即数组Huff的信息
{
printf("_____|_____|________|____________|___________|___________|\n");
printf("%4c |%4d | %5d | %10d |%10d |%10d |\n",Huff[i].ch, i, Huff[i].weight,
Huff[i].lchild,Huff[i].rchild, Huff[i].parent);
}
printf("_________________________________________________________|\n");
if((fp=fopen("d:\\hfmtree.txt","w"))==NULL)//建立hfmtree文件
{
printf("cannot open the file of hfmtree\n");
}
else
{
fprintf(fp,"zifu Huff weight lchild rchild parent \n");
for(i=0;i<2*n-1;i++)
{
fprintf(fp,"%3c %3d %5d %10d %10d %10d \n",Huff[i].ch,i, Huff[i].weight,
Huff[i].lchild,Huff[i].rchild, Huff[i].parent);
}
fclose(fp);
}
}
void HuffmanCode(HNode Huff[],int n) //生成哈夫曼编码
{
FILE *fw;
HCode HuffCode[MAXSIZE/2],cd;
// MAXSIZE/2为叶结点的最大个数
int i,j,c,p;
for(i=0;i<n;i++) //求每个叶结点的哈夫曼编码
{
HuffCode[i].weight=Huff[i].weight;
cd.start=MAXBIT-1;
c=i;
p=Huff[c].parent;
while(p!=-1)
{
if(Huff[p].lchild==c)
cd.bit[cd.start]=0;
else
cd.bit[cd.start]=1;
cd.start--;
c=p;
p=Huff[c].parent;
}
for(j=cd.start+1;j<MAXBIT;j++)
//保存该叶结点字符的哈夫曼编码
HuffCode[i].bit[j]=cd.bit[j];
HuffCode[i].start=cd.start;
//保存该编码在数组bit中的起始位置
}
printf("字母哈夫曼编码如下:\n");
printf("-----|--------|--------|-------|\n");
printf("zifu |HuffCode| weight | bit |\n"); //输出数组HuffCode的有关信息
printf("-----|--------|--------|-------|\n");
for(i=0;i<n;i++) //输出各叶结点对应的哈夫曼编码
{
printf("%4c |%5d |%4d | ",Huff[i].ch,i,HuffCode[i].weight);
for(j=HuffCode[i].start+1;j<MAXBIT;j++)
printf("%d |",HuffCode[i].bit[j]);
printf("\n");
printf("-----|--------|--------|-------|\n");
}
if((fw=fopen("d:\\CodeFile.txt","w"))==NULL)//建立codeFile文件
printf("cannot open the file of CodeFile\n");
else
{
fprintf(fw,"zifu HuffCode weight bit \n");
for(i=0;i<n;i++)
{
fprintf(fw,"%4c%5d%8d ",Huff[i].ch,i,HuffCode[i].weight);
for(j=HuffCode[i].start+1;j<MAXBIT;j++)
fprintf(fw,"%d",HuffCode[i].bit[j]);
fprintf(fw,"\n");
}
}
fclose(fw);
}
void decode(HNode Huff[],int n)
//依次读入电文,根据哈夫曼树译码
{
FILE *fs;
int i,j=0;
char b[MAXSIZE];
i=2*n-2; //从根结点开始往下搜索
printf("【输入电文,并进行译码】\n");
getchar();
printf("输入发送的编码(以'2'为结束标志):\n");
gets(b);
printf("译码后的字符为:\n");
while(b[j]!='2')
{
if(b[j]=='0')
i=Huff[i].lchild; //走向左孩子
else
i=Huff[i].rchild; //走向右孩子
if(Huff[i].lchild==-1) //tree[i]是叶结点
{
printf("%c",Huff[i].ch);
i=2*n-2; //回到根结点
}
j++;
}
printf("\n");
if(Huff[i].lchild!=-1&&b[j]!='2')
//电文读完,但尚未到叶子结点
printf("\nERROR\n"); //输入电文有错
else
{
if((fs=fopen("d:\\textFile.txt","w"))==NULL)
//建立textFile文件
printf("cannot open the textFile.txt of CodeFile\n");
else
{
fprintf(fs,"译码后的字符为");
while(b[j]!='2')
{
if(b[j]=='0')
i=Huff[i].lchild; //走向左孩子
else
i=Huff[i].rchild; //走向右孩子
if(Huff[i].lchild==-1) //tree[i]是叶结点
{
fprintf(fs,"%c",Huff[i].ch);
i=2*n-2; //回到根结点
}
j++;
}
}
}
}
void menu()
{
printf("菜单如下\n");
printf("1-------建立哈夫曼树\n" );
printf("2-------进行哈夫编码\n");
printf("3-------进行哈夫译码\n");
printf("0-------程序退出\n");
}
int main()
{
HNode Huff[MAXSIZE];
int n,sel;
printf(" ——哈夫曼编码与译码——\n");
printf("Input numbers of leaf :\n"); //n为叶结点个数
scanf("%d",&n);
do
{
menu();
printf("请输入您的选择:\n");
scanf("%d",&sel);
switch(sel)
{
case 1:
HuffTree(Huff,n);//建立哈夫曼树
break;
case 2:
HuffmanCode(Huff,n); //生成哈夫曼编码
break;
case 3:
decode(Huff,n);//译码变代码
break;
}
}while(sel!=0);
return 0;
}
五.测试内容
用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAM IS MY FAVORITE”
字符
A B C D E F G H I J K L M
频度
64 13 22 32 103 21 15 47 57 1 5 32 20
字符
N O P Q R S T U V W X Y Z
频度
57 63 15 1 48 51 80 23 8 18 1 16 1
补充:在字母后输入了空格‘ ’这个字符,及其频度为1,对其进行编码。
六.运行结果
1.输入树结点:
2.选择1,输入结点权值,构建哈夫曼树
3.选择2,进行哈夫编码
4.根据自己要编译的话输入对应的编码,进行翻译
七.收获及体会
经过一个周的课程设计,我发现了我平时学习方面的许多不足。仅仅通过半学期的数据结构学习是不能够轻松完成任务的,我们必须在课外多多学习一些其他的函数来充实自己以及将书本上的一些思想融会贯通成为自己的知识。此外学习是一个不间断的过程,我们应该适时温故而知新,否则学过的东西会很快遗忘。就像这次的课设,编程之前我把数据结构书和C语言又前前后后的翻看了两遍,才回忆起好多原来学习过的知识,但在编程时仍然觉得很吃力。后来我们在网上搜到了一些程序,结果有好多我们没有学过的函数,为此我们又一边编程一边学习一些能够对程序起一些作用的函数,这样我们才勉强把程序编完。同时,让我体会到编程序难,调试程序更难,调试程序需要足够的耐心,在这个过程中需要我们一遍又一遍的修改数据或是结构,直到达到我们的目的。这个过程让我学到了很多知识,也让我觉得好有成就感。
一个周的课设已经完了,但是它带给我的冲击还没有散去,在接下来的日子里我会继续深入地学习c语言和数据结构等,仅仅有课本上的知识是远不能达到需求的,我应该多学习,多实践,希望能在学习上更上一层楼。只要相信自己能做到,自己就能做到,再难的程序,只要自己喜欢去做,就一定能取得成功,告诉了自己只有永不言弃,这就是我的原则;
八、参考文献
<<C语言程序设计>> <<数据结构>>