数据结构课程设计总结报告 哈夫曼编码译码
- 格式:doc
- 大小:297.65 KB
- 文档页数:23
《数据结构》课程设计实验报告题目哈夫曼编码/译码器学院数理与信息学院专业计算机科学与技术班级计科132学生姓名刘海澍201359225235周弘杰201359225228徐铭瑶201359225203指导教师编写日期目录1 问题描述.................................................................错误!未定义书签。
2 问题分析 (1)3 算法设计 (2)3.1抽象数据类型定义 (2)3.2模块划分 (3)4 详细设计 (4)4.1数据类型的定义 (4)4.2主要模块的算法描述 (4)4.3 流程图 (6)5 测试分析 (9)6 课程设计总结 (10)7 成员分工 (10)参考文献 (11)附录(源程序清单) (12)1.问题描述设计一个利用哈夫曼算法的编码和译码系统,重复地显示并处理以下项目,直到选择退出为止。
1) 初始化:键盘输入字符集大小n、n个字符和n个权值,建立哈夫曼树;2) 编码:利用建好的哈夫曼树生成哈夫曼编码;3) 输出编码;4)显示哈夫曼树;5)界面设计的优化;6) 设字符集及频度如下表:字符空格 A B C D E F频度4 9 23 2 17 15字符G H I J K频度1 2 3 3 42.问题分析(1)定义一个变量名为HTNode的结构体,用该结构体中的char data、int weight、int parent、int lchild、int rchild分别表示哈夫曼树中每个结点的权值、权重、双亲结点、左孩子、右孩子,再定义一个HTNode类型的数组ht[60]存放哈夫曼树;另外定义一个变量名为HCode的结构体,采用HCode类型变量的cd[start]~cd[n]存放当前结点的哈夫曼编码、最后定义一个HCode类型的数组hcd[30]的数组用于存放当前叶子结点ht[i]的哈夫曼编码。
(2)编写CodeInput(n,ht)函数并在函数中设置一个for(i=0;n;++)循环,首先输入n个字符,再编写一个for(j=0;j<n;j++)循环将n个字符保存到全局数组ch[]里,再利用函数中的for(i=0;i<n;i++)循环实现对这n个字符的权值的输入。
(3)编写CreateHT(ht,n)函数来构造一棵哈夫曼树,首先用一个for(i=0;<2*n-1;++)循环将所有2n-1个结点的parent、lchild、rchild域均置初值为-1;再用一个for(i=n;<2*n-1;++)循环来构造哈夫曼树,在该循环中首先令lnode 和rnode为最小权值的两个结点位置且其域值均为-1,再用一个for(k=0;k<=i-1;k++)循环在数组ht[30]中寻找权值最小并且尚未构造二叉树的结点中查找,由于只能在尚未构造二叉树的结点中查找,因此在for(k=0;k<=i-1;k++)循环中加入一个if(ht[k].parent==-1)语句来判断结点lnode和rnode是否已经在二叉树中,如果结点lnode和rnode在二叉树中,则结点lnode和rnode的parent 的值为其双亲结点在数组ht[30]中的序号就不会为-1了,在查找到ht[lnode]和ht[rnode]后将他们作为ht[i]的左右子树,ht[lnode]和ht[rnode]的双亲结点置为ht[i],且ht[i].weight=ht[lnode].weight+ht[rnode].weight。
如此处理下去,直到所有2n-1个结点处理完毕。
(4)编写CreateHCode(ht,hcd,n)函数来求哈夫曼编码,由于求哈夫曼编码只需求叶子结点的哈夫曼编码。
对于当前叶子结点ht[i],首先将对应的哈夫曼编码hcd[i]的start域值置初值n,找其双亲结点ht[f],若当前结点是双亲结点的左孩子结点,则在hcd[i]的cd数组中添加0,若当前结点是双亲的右孩子结点,则在hcd[i]的cd数组中添加1,将start域减1。
再对双亲结点进行同样的操作,直到无双亲结点为止,最后让start指向哈夫曼编码最开始字符。
因此在函数中设置一个for(i=0;i<n;i++)循环,在循环中设置一个while(f!=-1)循环语句来判断循环是否到了根结点,再在while(f!=-1)中设置一个if(ht[f].lchild==c)语句来判断该处理左孩子结点还是该处理右孩子结点。
最后用这个for(i=0;i<n;i++)循环来根据哈夫曼树求出哈夫曼编码。
(5)最后编写CodeOutput(n,hcd)函数,首先利用for(i=0;i<n;i++)循环和for(j=hcd[i].start;j<=n;j++)循环来实现所有字符的哈夫曼编码的输出;再利用for(i=0;i<n;i++)循环和for(j=hcd[i].start;j<=n;j++)循环来实现每个字符的序号和哈夫曼编码的输出。
将每个字符在哈夫曼树中的位置用sh[p+1][o]=ch[i+1]保存到全局数组sh[]中用以输出哈夫曼树。
(6)显示哈夫曼树(7)界面的优化3. 算法设计3.1抽象数据类型定义(1)树的抽象数据类型定义ADT Stack{数据对象:D={ai|ai∈ElemSet,i=1,2,...,n, n≥0}数据关系:若D为空集,则称为空树。
若D仅为一个数据元素,则R为空集,否则R={H},H是如下的二元关系:(1)再D中存在唯一的称为根的数据元素root,它在关系H下无前驱。
(2)若D-{root}<>空集,则存在一个划分D1,D2,···,Dm(m>0)。
(3)对应于D-{root}的划分,H-{<root,X1},···,<root,Xm>}有唯一的一个划分H1,H2,···,Hm(m>0)。
基本操作:InitTree(&T)操作结果:构造空树T。
DestroyTree(&T)初始条件:树T已存在。
操作结果:树T被销毁。
ClearTree(&T)初始条件:树T已存在。
操作结果:将树T清为空栈。
TreeEmpty(T)初始条件:树T已存在。
操作结果:若树T为空,则返回TRUE,否则FALSE。
TreeDepth(T)初始条件:树T已存在。
操作结果:返回T的深度。
Root(T)初始条件:树T已存在。
操作结果:返回树T的根。
3.2程序划分本程序包括四个部分:(1)主程序部分void main(){初始化;构造哈夫曼树;求哈夫曼编码;哈夫曼编码输出;哈夫曼树输出}(2)哈夫曼树部分——实现哈夫曼树的抽象数据类型(3)求哈夫曼编码部分——实现求哈夫曼编码算法的数据类型(4)打印哈弗曼树4. 详细设计4.1数据类型的定义(1)哈夫曼树类型//哈夫曼树类型typedef struct{//构造树char data;//结点权值int weight;//权重int parent;//双亲结点int lchild;//左孩子int rchild;//右孩子}HTNode;HTNode ht[60];(2)求哈夫曼编码类型typedef struct{char cd[30];//存放当前结点的哈弗曼编码int start;//cd[start]~cd[n]存放哈弗曼码}HCode;HCode hcd[30];4.2主要的算法描述(1)主函数int main(){int n;printf("请输入要输入的字符数量n:");//读入字符的个数while(scanf("%d",&n),n!=-1){//初始化printf("请输入n个字符和n个权值\n");CodeInput(n,ht);//输入数据函数,将读入n个字符和权重CreateHT(ht,n);//构造哈弗曼树CreateHCode(ht,hcd,n);//哈弗曼编码算法CodeOutput(n,hcd);}//打印哈弗曼编码,将打印出编码,和每一个字符的编码return 0;(2)构造哈夫曼树void CreateHT(HTNode ht[],int n)//构造一棵哈夫曼树{int i,k,lnode,rnode;int min1,min2;for (i=0;i<2*n-1;i++)//所有结点的相关域置初值-1ht[i].parent=ht[i].lchild=ht[i].rchild=-1;for (i=n;i<2*n-1;i++)//构造哈弗曼树{min1=min2=32767;//lnode和rnode为最小权值的两个结点位置lnode=rnode=-1;for (k=0;k<=i-1; k++)//在ht[]中查找权值的最小的两个结点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[lnode].parent=i; ht[rnode].parent=i;ht[i].weight=ht[lnode].weight+ht[rnode].weight;ht[i].lchild=lnode; ht[i].rchild=rnode;//ht[]作为双亲结点}}(3)利用哈夫曼编码算法哈夫曼编码void CreateHCode(HTNode ht[],HCode hcd[],int n){int i,f,c;HCode hc;for(i=0; i<n; i++)//根据哈弗曼树求哈弗曼编码{hc.start=n; c=i;f=ht[i].parent;while (f!=-1)//循环知道树的根结点{if(ht[f].lchild==c)hc.cd[hc.start--]='0';//处理左孩子结点elsehc.cd[hc.start--]='1';//处理右孩子结点c=f; f=ht[f].parent;}hc.start++;//start指向哈弗曼编码最开始的位置hcd[i]=hc;}}(4)打印哈夫曼编码并确定每个编码对应的哈夫曼树坐标打印哈夫曼编码void CodeOutput(int n,HCode hcd[]){int i, j, p=0, q=0;char zh[20][20],x[20],y[20];printf("所有字符的哈弗曼编码\n");for (i=0;i<n;i++){for(j=hcd[i].start;j<=n;j++){printf("%c",hcd[i].cd[j]);}}printf("\n");for (i=0;i<n;i++){printf("序号%d %c: ", i, ch[i+1]); for(p=0,q=0,j=hcd[i].start;j<=n;j++) {printf("%c",hcd[i].cd[j] );if(hcd[i].cd[j] == '0'){p++; q=0;}else if(hcd[i].cd[j] == '1'){p++; q+=2;}}sh[p][q]=ch[i+1]; printf("(%d,%d)\n",p,q); printf("\n"); } }4.3 流程图(1)主函数流程如图4.3-14.3-1 主函数流程图 (2)构造哈夫曼流程图,如图4.3-2开始初始化:键盘输入字符集大小n n 个字符和 和n 个权值哈夫曼树的创建程序创建哈夫曼树哈夫曼编码译码程序求哈夫曼编码n>=0&&n<=204.3-2构造哈弗曼树流程图(3)哈弗曼编码流程图,如图4.3-34.3-3 哈弗曼编码流程图(4)打印哈弗曼编码流程图,如图4.3-4结果分析:首先确定要输入的字符数量n为8,然后分别输入这8个字符和这8个字符的权值,且输入的这8个字符分别为B、C、D、E、F、G、H、I而这8个字符对应的权值分别为13、22、32、103、21、15、47、57。