哈夫曼树的编码与译码.docx
- 文档编号:17727321
- 上传时间:2023-04-24
- 格式:DOCX
- 页数:25
- 大小:278.03KB
哈夫曼树的编码与译码.docx
《哈夫曼树的编码与译码.docx》由会员分享,可在线阅读,更多相关《哈夫曼树的编码与译码.docx(25页珍藏版)》请在冰豆网上搜索。
哈夫曼树的编码与译码
第1章概要设计
1.1题目的内容与要求
内容:
打开一片英文文章,统计该文章中每个字符出现的次数,然后以它们作为权值,对每一个字符进行编码,编码完成后再对其编码进行译码。
要求:
1、独立完成系统的设计、编码和调试。
2、系统利用C语言实现。
3、按照课程设计规范书写课程设计报告。
1.2总体结构
本程序主要分为六个模块(功能模块图见图1.1):
主模块,文件读入模块,字符编码模块,全文编码模块,译码模块。
主模块:
程序的主体部分,分别调用各个模块,实现各项功能。
文件读入模块:
从磁盘读入文件。
功能选择模块:
提供用户选择界面。
字符编码模块:
对每个出现的字符进行编码。
全文编码模块:
对整篇文章进行编码。
全文译码模块:
将已有编码译成文章,使之可以直接被读出。
图1.1功能模块图
第2章算法分析
2.1核心算法思想
哈夫曼编码(HuffmanEncoding)是可变字长编码。
编码时借助哈夫曼树,也即带权路径长度最小的二叉树,来建立编码。
2.2算法描述
2.2.1哈夫曼树和哈弗曼编码存储结构
Typedefstruct{
Unsignedintweight;
Unsignedintparent,lchild,rchild;
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
Typedefchar**HuffmanCode;//动态分配数组存储哈弗曼编码表
2.2.2求哈夫曼编码的算法
VoidHuffmanCoding(HuffmanTree&HT,HuffmanCode&HC,int*w,intn){
//w存放n个字符的权值(均)0),构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC。
If(n<=1)
Return;
M=28n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用
For(p=HT,i=1;i<=n;++i,++p,++w)
*p={*w,0,0,0};
For(;i<=m;++i,++p)
*p={0,0,0,0};
For(i=n+1;i<=m;++i)
{
//在HT[1..i-1]选择parent为0且weight最小的两个结点,其序列号分别为s1和s2。
Select(HT,i-1,s1,s2);
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
//---从叶子到根逆向求每个字符的哈夫曼编码----
HC=(HuffmanCode)malloc(n+1)*sizeof(char*));
Cd=(char*)malloc(n*sizeof(char));
Cd[n-1]=”\0”;
For(i=1;i<=n;++i)
{
Start=n-1;
For(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)
If(HT[f].lchild==c)
Cd[--start]=”\0”;
Else
Cd[--start=”1”;
HC[i]=(char*)malloc(n-start)*sizeof(char));
Strcpy(HC[i],&cd[start]);
}
Free(cd);
}
第3章详细设计
3.1主模块
控制整个程序的运行,通过主函数模块分别调用各个模块,实现各项功能,流程如图2.1所示。
图3.1主模块流程图
注:
1、运行程序后即可出现系统读入的文章以及用户选择界面;
2、用户可根据需要选择字符编码、全文编码、译码和退出;
3.2字符编码模块
对从文件中读出的每个出现的字符以其出现次数为权值进行哈夫曼编码,将得到的编码存至根文件下的“字符编码.txt”中,其流程图如图2.2..1和2.2.2所示。
图3.2.1字符编码模块流程图1
注:
此流程图为构造哈夫曼树的过程,以从文本文件读入的字符的次数为权值对每个结点赋值,构造哈夫曼树
图3.2.2字符编码模块流程图2
注:
此流程图为对字符进行哈夫曼编码的过程,将字符转化为哈弗曼编码。
3.3全文编码编码模块
从文本文档中读取整篇英文文章,并根据每个字符的编码结果对整篇文章进行哈弗曼编码并将得到的编码存入根文件下的“哈夫曼码.txt”文件中,以方便之后的过程调用。
其流程图如图2.3所示。
图3.3全文编码模块流程图
注:
此过程主要运用了对文件的操作方面的知识,使之能对全文进行编码并且将编码写入文件中保存。
3.4全文译码模块
本模块主要是根据字符模块得到的每个出现的字符对应的哈夫曼编码,再结合从“哈夫曼码.txt”文件中读出全文的哈夫曼编码将哈弗曼编码翻译为字符并写入文件“破译文件.txt”中并打印出来。
其流程图如图2.4所示。
图3.4全文译码模块流程图
第4章系统实现
4.1错误分析
在此程序调试过程中主要遇到以下几类问题:
1、本程序用到的外部函数较多,在调用时一定要注意传入的参数是否符合函数的定义,而且位置也不能错,这是引用函数要注意的一点。
2、在函数内部有时需要多定义参数,注意参数的作用域,而且注意传引用调用和传值调用的区别,不能不正确的修改实参的值,否则会导致程序运行的错误。
3、本程序运用了对文件进行操作,一定要注意在操作文件是文件的位置,我在做次程序是就是因为操作的文件位置错了导致程序无法正常运行。
5、刚开始时清屏函数运用出错,导致操作界面消失,使用户无法操作,因此,在使用一些函数是一定要注意。
4.2实现结论
程序能够正常运行,能够进行哈夫曼编码和哈夫曼译码操作。
4.3运行结果
在根文件夹下建立“文本文件.txt”,输入一段英文文章并以 “#”结束(以#作为结束标识符),保存。
运行程序后会出现如下界面(图4.3.1)共用户选择:
图4.3.1程序运行截图1
选择1,会显示每个字符的哈夫曼编码,显示以下界面(图4.3.2)供用户选择:
图4.3.2程序运行截图2
选择2,会显示对原文进行哈弗曼编码得到的哈弗曼编码,显示以下界面(图4.3.3)供用户选择:
图4.3.3程序运行截图3
选择3,会显示用哈夫曼编码翻译成的字符,显示以下界面(图4.3.4)供用户选择:
图4.3.4程序运行截图4
选择4,退出系统,显示以下界面(图4.3.5)。
图4.3.5程序运行截图5
经测试,程序可以正常运行,本次测试圆满完成。
参考文献
[1]严蔚敏.数据结构(C语言版).清华大学出版社,2007
[2]苏仕华.数据结构课程设计.机械工业出版社,2007
[3]谭浩强.C语言程序设计教程.高等教育出版社,2006
附录源程序
#include
#include
#include
#include
#defineN1000//输入字符的最多数目,可根据具体需要而定
typedefstruct//哈夫曼树和哈夫曼编码存储结构
{
intweight;//权重
intparent,lchild,rchild;//左右孩子
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
typedefchar**HuffmanCode;//动态分配数组存储哈夫曼编码表
intfileopen()//打开原文件"文本文件.txt"
{
FILE*fp;
if((fp=fopen("文本文件.txt","r"))==NULL)
{
printf("cannotopenthisfile\n");
exit
(1);
}
fclose(fp);
return0;
}
voidprint1(char*name)//逐字符打印文件(以字符为单位,以#作为结束标志)
{
FILE*fp;
charc;
if((fp=fopen(name,"rb"))==NULL)
{
printf("cannotopenthisfile\n");
return;
}
c=fgetc(fp);
while(c!
='#')
{
printf("%c",c);
c=fgetc(fp);
}
}
charkind[N]={‘\0'};//字符种类
intnum[N]={0};//字符相应的个数
intcount=0;//字符总数
voidCalculate()//统计字符种类及个数,用来作为哈夫曼编码时的权重
{
charc;
inti=1,k;
FILE*fp;
if((fp=fopen("文本文件.txt","rb"))==NULL)
{
printf("cannotopenthisfile\n");
return;
}
c=fgetc(fp);
while(c!
='#')
{
for(k=0;k
if(c==kind[k])
{
num[k]++;
break;
}
if(k>=i&&kind[k-1]!
=c)
{
kind[i]=c;
num[i]++;
i++;
}
c=fgetc(fp);
}
count=i-1;
}
ints1,s2;//标记select函数调用时返回的最值
structcharcode
{
chardata;
charcode[20];
}Code[N],d[N],e[N];//结构体包括字符元素和字符相应的代码
voidHuffmanCoding()//哈夫曼编码,求最小生成树及字符的哈夫曼编码
{
voidselect(HuffmanTreeHu,intj);
HuffmanTreeHT;//哈夫曼树
HuffmanCodeHC;//哈夫曼编码
intm,i,start,f,c;
char*cd;
FILE*fp;
if(count<=1)
return;
m=2*count-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用
for(i=1;i<=count;i++)
HT[i].weight=num[i];
for(i=1;i<=m;i++)//初始化
{
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(i=count+1;i<=m;i++)//建哈夫曼树,在HT[1..i-1]选择parent为0且weight最小的两个结点,其序列号分别为s1和s2.
{
select(HT,i-1);
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
//从叶子到根逆向求每个字符的哈夫曼编码
HC=(HuffmanCode)malloc((count+1)*sizeof(char*));//分配n个字符编码的头指针向量
cd=(char*)malloc(count*sizeof(char));//分配求编码的工作空间
cd[count-1]='\0';//编码结束符
for(i=1;i<=count;i++)//逐个字符求哈夫曼编码
{
start=count-1;//编码结束符位置
for(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)//从叶子到根逆向求编码
if(HT[f].lchild==c)
cd[--start]='0';
else
cd[--start]='1';
HC[i]=(char*)malloc((count-start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//从cd复制编码(串)到HC
}
if((fp=fopen("字符编码.txt","wb"))==NULL)
{
printf("cannotopenthisfile\n");
return;
}
for(i=1;i<=count;i++)
{
Code[i].data=kind[i];
strcpy(Code[i].code,HC[i]);
fwrite(&Code[i],sizeof(structcharcode),1,fp);
}
fclose(fp);
}
voidselect(HuffmanTreeHu,intj)//在HT[1~j]选择parent为0且weight最小的两个结点,序号为s1,s2
{
intk=1,t;
while(Hu[k].parent!
=0)
k++;//找第一个parent不为0的结点
for(t=k;t<=j;t++)
if(Hu[t].parent==0&&Hu[t].weight k=t; s1=k; k=1; while(Hu[k].parent! =0||k==s1) k++; for(t=k;t<=j;t++) if(Hu[t].weight =s1) k=t; s2=k; } voidprint2(char*name)//结构体structcharnode的文件的打印 { FILE*fp; inti=1; if((fp=fopen(name,"rb"))==NULL) { printf("cannotopenthisfile\n"); return; } while(! feof(fp)) { fread(&e[i],sizeof(structcharcode),1,fp); printf("%c--%s\n",e[i].data,e[i].code); i++; } } voidTransform()//使文本文件转化成哈夫曼编码 { FILE*fp1,*fp2,*fp3; inti; charc; if((fp1=fopen("字符编码.txt","rb"))==NULL) { printf("cannotopenthisfile\n"); return; } if((fp2=fopen("文本文件.txt","rb"))==NULL) { printf("cannotopenthisfile\n"); return; } if((fp3=fopen("哈夫曼码.txt","wb"))==NULL) { printf("cannotopenthisfile\n"); return; } for(i=0;i fread(&d[i],sizeof(structcharcode),1,fp1); while(! feof(fp2)) { c=fgetc(fp2); for(i=0;i if(d[i].data==c) { fputs(d[i].code,fp3); break; } } fputc('#',fp3);//使该文件也可用print1 fclose(fp3); } voidTranslate()//把码本文件转化为可读的破译文件 { FILE*fp1,*fp2,*fp3; inti,j,k; charc[20]={'\0'}; if((fp1=fopen("哈夫曼码.txt","rb"))==NULL) { printf("cannotopenthisfile\n"); return; } if((fp2=fopen("字符编码.txt","rb"))==NULL) { printf("cannotopenthisfile\n"); return; } if((fp3=fopen("破译文件.txt","wb"))==NULL) { printf("cannotopenthisfile\n"); return; } for(i=1;i<=count;i++) fread(&d[i],sizeof(structcharcode),1,fp2); i=0; c[i]=fgetc(fp1); while(c[i]! ='#') { for(k=1;k<=count;k++) if(! strcmp(c,d[k].code)) { fputc(d[k].data,fp3); for(j=0;j<=i;j++)c[j]='\0'; i=-1; break; } i++; c[i]=fgetc(fp1); } fputc('#',fp3); fclose(fp1); fclose(fp2); fclose(fp3); } voidmain() { intchoice; fileopen(); printf("系统读入的英文文章是: "); print1("文本文件.txt"); printf("\n"); printf("====================================================================\n"); printf("\t\t*******欢迎使用哈夫曼编译码系统*******\n"); printf("\t\t*****1.求每个字符对应的哈夫曼编码*****\n"); printf("\t\t*****2.求原文本的哈夫曼编码***********\n"); printf("\t\t*****3.将哈夫曼编码翻译为字符*********\n"); printf("\t\t*****4.退出哈夫曼编译码系统***********\n"); printf("====================================================================\n"); printf("\t\t*********请选择你要进行的操作*********\n"); Calculate();//对文本文件进行统计,计算字符种类和权重 scanf("%d",&choice); switch(choice) { case1: { system("cls");//清屏 HuffmanCoding();//建立哈夫曼树,计算出哈夫曼编码 printf("每个字符相应的哈夫曼编码是: \n");//打印每个字符对应的哈夫曼编码 print2("字符编码.txt"); printf("====================================================================\n"); main(); } case2: { system("cls"); Transform();//把原文本文件转化为哈夫曼编码并打印 printf("原文的哈夫曼编码为: \n"); print1("哈夫曼码.txt"); printf("\n"); printf("====================================================================\n"); main(); } case3: { system("cls"); Translate();//把哈夫曼码转化为可读的破译文本并打印 printf("用哈夫曼码求出的破译文件为: \n"); print1("破译文件.txt"); printf("\n"); printf("====================================================================\n"); main(); } case4: { printf("\t\t\t***谢谢使用! ***\n"); exit(0); } default: { system("cls"); printf("对不起,没有这个选项,请重新输入! \n"); main(); } } } 课程设计总结: 通过近两周的课程设计使我对哈夫曼树以及哈夫曼编码有了更深的认识和理解,也使我更加明白哈夫曼编码译码在信息技术中的重要性和地位。 首先我谈谈我在设计期间我遇到的难点。 开始的时候,代码中有许多的错误,特别是有一个“无法找到文件”的错误让我束手无策,最后还是屏蔽了定义的四个头文件然后慢慢地改正错误才让我又看到了希望。 然后在实现文章的读入时,由于对文件不是太熟悉,只好翻开C语言书本仿照其模式编写,但后来进入了死循环,最后的解决方式是在main函数里有一个控制语句使用不正确。 其次许多的错误让我明白了一个道理---细心是非常重要的。 同时,对于编程者而言,思路清晰是相当重要的。 在适当的时候和同学一起交流探讨是一个十分好的学习机会。 请教老师也很重要,因为毕竟我们是新手,对于某些问题很难弄清楚。 感谢指导老师对我的耐心指导! 这次课程设计不但让我学得了一些编程知识,还学会了系统的做一份课程设计报告,学会了如何截图,学会了如何更好的画流程图,明白了做事情只有认真,才能真正做得更好! 通过这次课程设计,我看清楚了自己的编程功底和动手能力还不如人意,这主要是平时实践太少的缘故。 我想,在即将到来的寒假中,我会努力尝试编写一些程序。 争取提高自己的编程水平。 最后再次感谢指导老师的耐心细致的指导使我这次课设圆满完成。 在今后的学习工作中更要认真,使自己的解决问题的能力更上一层楼! 指导教师评语: 指导教师(签字): 年月日 课程设计成绩
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 哈夫曼树 编码 译码