哈呼曼编译器设计说明书.docx
《哈呼曼编译器设计说明书.docx》由会员分享,可在线阅读,更多相关《哈呼曼编译器设计说明书.docx(18页珍藏版)》请在冰豆网上搜索。
哈呼曼编译器设计说明书
*******************
实践教学
*******************
兰州理工大学
计算机与通信学院
2007年春季学期
算法与数据结构课程设计
题目:
哈夫曼编译码器设计
专业班级:
05计算机科学与技术3班
姓名:
徐玉霞
学号:
05240317
指导教师:
李睿
成绩:
_____________________
目录
摘要2
前言3
正文4
1.采用类c语言定义相关的数据类型4
2.各模块的伪码算法4
3.函数的调用关系图7
4.调试分析7
5.测试结果8
6.源程序(带注释)10
参考文献18
致谢19
附件Ⅰ部分源程序代码20
摘要
该设计是对输入的一串电文字符实现哈夫曼编码,再对哈夫曼编码生成的代码串进行译码,输出电文字符串。
在该设计中把数据压缩过程称为编码,解压缩的过程称为译码。
此程序中建立了哈夫曼树,并利用建好的哈夫曼树对文件中的正文进行编码,对文件中的代码进行译码,显示输出等功能。
关键词:
哈夫曼树,哈夫曼编码,哈夫曼译码。
前言
哈夫曼编码的应用很广泛,利用哈夫曼树求地的二进制编码称为哈夫曼编码。
哈夫曼树中从根到每个叶子都有一条路径,对路径上的各分支约定:
指向左子树的分支表示“0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为对应的编码,这就是哈夫曼编码。
我们在对一些问题进行求解时,会发现有些问题很难找到规律,或者根本无规律可寻。
对于这样的问题,可以利用计算机运算速度快的特点,先搜索查找所有可能出现的情况,再根据题目条件从所有可能的情况中,删除那些不符合条件的解。
由哈夫曼算法的定义可知,初始森林中共有n棵只含有根结点的二叉树。
算法的的第二步是:
算法的的第二步是:
将当前森林中的两棵根结点权值最小的二叉树,合并成一棵新的二叉树,每合并一次,森林中就减少一棵树,产生一个新结点。
则要进行n-1次合并,所以共产生n-1个新结点。
由此可知,最终求得的哈夫曼树中一共有2n-1个结点。
其中,n个结点是初始森林中的n个孤立结点。
并且哈夫曼树中没有度数为1的分支的结点。
采用哈夫曼编码方案,即应用哈夫曼树构造使电文的编码总长最短的编码方案。
正文
1.采用类C语言定义相关的数据类型
(1)哈夫曼树的存储结构定义为:
typedefstruct/*字符集的元素结构*/
{chardata;
intweight;//权值
}elemtype;
typedefstruct/*哈夫曼树的元素结构*/
{chardata;
intflag;
intweight;//权值
intparent,lchild,rchild;//左右孩子及双亲指针
}htnode,*huffmantree;//动态分配数组存储哈夫曼树
typedefchar**HuffmanCode;//动态分配数组存储哈夫曼编码表
(2)哈夫曼树的存储结构描述:
#include
#include
#include
#definen27/*字符集的容量*/
#defineMAXVALUE1000/*权值的最大值*/
#defineMAXNODE35//哈夫曼树最多节点数
#defineMAXD100
2.各模块的伪码算法:
(1)构造哈夫曼树的算法
voidChuffmanTree(HuffmanTreeHT,HuffmancodeHc,intcnt[],charstr[])
{/*构造哈夫曼树HT,cnt中存放每类字符在电文中出项的频率,str中存放电文中不同类的字符*/
inti,s1,s2;
for(i=1;i<=2;*num-1;i++)
{
HT[i].lchild=0;HT[i].rchild=0;
HT[i].parent=0;HTweight=0;
}
for(i=1;i<=2*num;i++)/*输入num个结点的权值*/
HT[i].weight=cnt[i];
for(i=num+1;i<=2*num-1;i++)
{/*在HT[1..i-1]中选择parent为0且权值最小的两个结点*/
selext(Ht,i-1,s1,s2);
HT[s1].parent=i;HT[s2].parent=i;
Ht[i].lchild=s;HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
for(i=0;i<=num;i++)/*输入字符集中字符*/
HC[i].ch=str[i];
i=1;while(i<=num)
printf("字符&c,次数为:
%d\n",HC[i].ch,cnt[i++]);
}
(2)求哈夫曼编码(huffmancode)算法:
HuffmanCodehuffmancode(huffmantreeht)
{char*cd;
intc,i,f,start;
HuffmanCodeHC;
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
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';
elsecd[--start]='1';
HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//从cd复制编码(串)到HC
}
for(i=1;i<=n;i++)
puts(HC[i]);
free(cd);//释放工作空间
return(HC);
}
(3)求测试数据的编码(encoding)算法:
voidencoding(huffmantreeht,char**hc)//根据哈夫曼树HT求哈夫曼编码表HC
{inti,j,k;
charsstring[MAXNODE];
clrscr();
flushall();
printf("inputhuffmancode\neg.:
thisisprogrammaismyfavorite:
\n");
gets(sstring);
for(i=0;i{for(j=1;j<=n;j++)
if(sstring[i]==ht[j].data)
{k=j;
break;
}
(4)哈夫曼译码算法:
voiddecoding(huffmantreeht)//代码文件的译码
{chara[MAXD];
intm,i;
clrscr();
scanf("%s",a);
m=2*n-1;
for(i=0;a[i]!
='\0';i++)
{if(a[i]=='0')m=ht[m].lchild;
elsem=ht[m].rchild;
if(m<=n)
{printf("%c",ht[m].data);
m=2*n-1;
}
}
}
3.函数调用关系图:
4.调试分析:
a.调试中遇到的问题及对问题的解决方法:
在对源程序进行调试时,当输入字符后,应正确判断所输入的字符是否满足程序的需求,且保正所输入字符后所输出语句功能唯一。
则在源程序中加入了一条while()判断语句,若while()判断语句中表达式成立,就重新获取一个字符,然后对while()语句进行重新判断,直到while()判断语句中表达式不成立,就执行switch()语句中于其输入字符相对应的case’’语句.其程序语句如下:
while(ch!
='r'&&ch!
='c'&&ch!
='e'&&ch!
='d'&&ch!
='q')
ch=getchar();/*选取功能*/
switch(ch)
{case'r':
readdata(w);break;/*初始化(readdata),读取数据*/
case'c':
ht=createhuff(w);break;/*建立哈夫曼树(createhuff)*/
case'e':
hc=huffmancode(ht);encoding(ht,hc);break;
/*求字符集的哈夫曼编码(huffmancode)和求测试数据的编码(encoding)*/
case'd':
decoding(ht);break;/*译码(decoding)*/
case'q':
return;/*程序结束,返回*/
}
b.算法的时间复杂度和空间复杂度
时间复杂度:
建立哈夫曼树:
O(n³)
对文件中正文进行编码:
O(n2)
对文件中的代码进行译码:
O(n)
5测试结果
对下列给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:
“THISPROGRAMISMYFAVORITE“。
字符空格ABCDEFGHIJKLM
频度1866413223210321154757153220
字符NOPQRSTUVWXYZ
频度5763151485180238181161
输入r后:
输入c后:
输入e后:
6.源程序(带注释)
#include
#include
#include
#definen27/*字符集的容量*/
#defineMAXVALUE1000/*权值的最大值*/
#defineMAXNODE35//哈夫曼树最多节点数
#defineMAXD100
typedefstruct/*字符集的元素结构*/
{chardata;
intweight;
}elemtype;
typedefstruct/*哈夫曼树的元素结构*/
{chardata;
intflag;
intweight;//权值
intparent,lchild,rchild;//左右孩子及双亲节点
}htnode,*huffmantree;//动态分配数组存储哈夫曼树
typedefchar**HuffmanCode;//动态分配数组存储哈夫曼编码表
voidreaddata(elemtype*w);
huffmantreecreatehuff(elemtype*w);
char**huffmancode(huffmantreeht);
voidencoding(huffmantreeht,char**hc);
voiddecoding(huffmantreeht);
//初始化readdata,读取数据
voidreaddata(elemtype*w)
{inti;
w[0].data='';w[0].weight=186;
w[1].data='a';w[1].weight=64;
w[2].data='b';w[2].weight=13;
w[3].data='c';w[3].weight=22;
w[4].data='d';w[4].weight=32;
w[5].data='e';w[5].weight=103;
w[6].data='f';w[6].weight=21;
w[7].data='g';w[7].weight=15;
w[8].data='h';w[8].weight=47;
w[9].data='i';w[9].weight=57;
w[10].data='j';w[10].weight=1;
w[11].data='k';w[11].weight=5;
w[12].data='l';w[12].weight=32;
w[13].data='m';w[13].weight=20;
w[14].data='n';w[14].weight=57;
w[15].data='o';w[15].weight=63;
w[16].data='p';w[16].weight=15;
w[17].data='q';w[17].weight=1;
w[18].data='r';w[18].weight=48;
w[19].data='s';w[19].weight=51;
w[20].data='t';w[20].weight=80;
w[21].data='u';w[21].weight=23;
w[22].data='v';w[22].weight=8;
w[23].data='w';w[23].weight=18;
w[24].data='x';w[24].weight=1;
w[25].data='y';w[25].weight=16;
w[26].data='z';w[26].weight=1;
for(i=0;i<27;i++)
{printf("%c",w[i].data);
printf("%d\t",w[i].weight);
}
return;
}
huffmantreecreatehuff(elemtype*w)
{inti,j,x1,x2;
intm1,m2,m;
huffmantreeHT,p;
m=2*n-1;
HT=(huffmantree)malloc((m+1)*sizeof(htnode));//0号单元未用
p=HT;p++;
for(i=1;i<=m;i++,p++,w++)//初始化HT
{if(i<=n)
{
p->data=w->data;
p->weight=w->weight;
}
else
{
p->data='\0';
p->weight=0;
}
p->parent=0;
p->lchild=0;
p->rchild=0;
p->flag=0;
}
for(i=1;i{m1=m2=MAXVALUE;
x1=x2=0;
p=HT;
for(j=1;j<=n-1+i;j++)
{
if(p[j].weight{m2=m1;
x2=x1;
m1=p[j].weight;
x1=j;
}
elseif(p[j].weight{m2=p[j].weight;
x2=j;
}
}
p[x1].parent=n+i;
p[x2].parent=n+i;
p[x1].flag=1;
p[x2].flag=1;
p[n+i].weight=p[x1].weight+p[x2].weight;
p[n+i].lchild=x1;
p[n+i].rchild=x2;
}
clrscr();
for(i=1;i<=m;i++)
{printf("%d,%c,%d,%3d,%3d,%3d,%3d\n",i,p[i].data,p[i].flag,p[i].weight,p[i].parent,p[i].lchild,p[i].rchild);
if(i%15==0){
getch();
clrscr();}
}
return(HT);
}
HuffmanCodehuffmancode(huffmantreeht)
{char*cd;
intc,i,f,start;
HuffmanCodeHC;
//从叶子到根逆向求哈夫曼编码
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符的头指针向量
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';
elsecd[--start]='1';
HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//从cd复制编码串到HC
}
for(i=1;i<=n;i++)
puts(HC[i]);
free(cd);//释放工作区间
return(HC);
}
voidencoding(huffmantreeht,char**hc)
{inti,j,k;
charsstring[MAXNODE];
clrscr();
flushall();
printf("inputhuffmancode\neg.:
thisisprogrammaismyfavorite:
\n");
gets(sstring);
for(i=0;i{for(j=1;j<=n;j++)
if(sstring[i]==ht[j].data)
{k=j;
break;
}
if(k<=0||k>MAXNODE)
{printf("thereisn'tNO.%dchar\n",i);
continue;
}
printf("%c\tweight=%3d\t%s\n",ht[k].data,ht[k].weight,hc[k]);
}
}
voiddecoding(huffmantreeht)
{chara[MAXD];
intm,i;
clrscr();
scanf("%s",a);
m=2*n-1;
for(i=0;a[i]!
='\0';i++)
{if(a[i]=='0')m=ht[m].lchild;
elsem=ht[m].rchild;
if(m<=n)
{printf("%c",ht[m].data);
m=2*n-1;
}
}
}
main()
{elemtypew[n];/*字符和频度集合类型*/
charch;
huffmantreeht;/*哈夫曼树类型*/
HuffmanCodehc;/*编码类型*/
ch=getchar();
while(ch!
='q')/*当键入’q’时程序运行结束*/
{
while(ch!
='r'&&ch!
='c'&&ch!
='e'&&ch!
='d'&&ch!
='q')
ch=getchar();/*选取功能*/
switch(ch)
{case'r':
readdata(w);break;/*初始化(readdata),读取数据*/
case'c':
ht=createhuff(w);break;/*建立哈夫曼树(createhuff)*/
case'e':
hc=huffmancode(ht);encoding(ht,hc);break;
/*求字符集的哈夫曼编码(huffmancode)和求测试数据的编码(encoding)*/
case'd':
decoding(ht);break;/*译码(decoding)*/
case'q':
return;/*程序结束,返回*/
}
ch=getchar();
}
}
总结
在这三周的数据结构课程设计中,我的题目是:
哈夫曼编译码器设计,这三周课程设计中,通过该题目的设计过程,我加深了对树的数据结构以及二叉树的逻辑结构,存储结构的理解,对树的数据结构上基本运算的实现有所掌握,对课本中所学的各种数据结构进一步理解和掌握,学会了如何把学到的知识用于解决实际问题,锻炼了自己动手的能力。
一个人要完成所有的工作是非常困难和耗时的。
在以后的学习中我会更加注意自己各个方面的能力的协调发展。
在课程设计时我遇到了很多的问题,在老师的帮助,和对各种资料的查阅中,将问题解决,培养了我自主动手,独立研究的能力,为今后在学习工作中能更好的发展打下了坚实的基础。
三周的课程设计很短暂,但其间的内容是很充实的,在其中我学习到了很多平时书本中无法学到的东西,积累了经验,锻炼了自己分析问题,解决问题的能力,并学会了如何将所学的各课知识融会,组织,来配合学习,三周中我收益很大,学到很多。
参考文献
1.严蔚敏,陈文博编著.数据结构及应用算法教程,北京:
清华大学出版社,2001
2.苏仕华主编.数据结构自学辅导.北京:
清华大学出版社,2002
3.苏仕华主编.数据结构与算法解析.合肥:
中国科学技术大学生出版社,2004
4.谭浩强编著.C程序设计.北京:
清华大学出版社,1991
附件Ⅰ部分源程序代码
栈的应用——用栈来实行编译(左右括号配对)
算法:
voidpush(stacknode*st,elemtypex)/*将元素x入栈*/
{
if(st->top==m)/*判断栈ST是否已满*/
printf("thestackisoverflow!
\n");
else
{
st->top=st->top+1;
st->stack[st->top]=x;
}
}
voidpop(stacknode*st)/*将栈的栈顶元素出栈*/
{
st->top=st->top-1;
}
源程序:
#definem20
#defineelemtypechar
typedefstruct/*栈类型的定义*/
{
elemtyp