哈夫曼编码译码报告
烟台大学计算机与控制工程学院课程设计
(数据结构与OOP)
设计题目:
班级
姓名
学号
指导教师
成绩
年月日
目录
1 题目 (3)
1.1 问题描述 (3)
1.2 基本要求 (3)
1.3 进一步完成 (3)
2 内容 (3)
2.1 基本需求 (3)
2.2. 我的设计 (4)
3 算法设计 (4)
3.1 数据的存储结构 (4)
3.1.1 存放哈夫曼树的存储结构: (4)
3.1.2 存放哈夫曼编码的存储结构: (4)
3.1.3 存放哈夫曼树每个节点位置的存储结构: (5)
3.2 生成哈弗曼树的算法 (5)
3.3 生成哈弗曼编码的算法 (6)
3.4 译码的算法 (8)
3.5 打印哈弗曼树的算法 (9)
3.6 其他算法 (10)
4 程序正确性验证 (10)
4.1 输入数据的控制 (10)
4.2 打印哈弗曼树 (11)
4.3 哈弗曼编码 (11)
4.4 哈弗曼译码 (12)
5 遇到的问题 (12)
6 课程设计的主要收获 (12)
7 对今后课程设计的建议 (12)
1 题目
1.1 问题描述
设计一个利用哈夫曼算法的编码和译码系统,重复地显示并处理项目,直到选择退出为止。
1.2 基本要求
1) 将权值数据存放在数据文件(文件名为data.txt,位于执行程序的当前目中
2) 分别采用动态和静态存储结构
3) 初始化:键盘输入字符集大小n、n个字符和n个权值,建立哈夫曼树
4) 编码:利用建好的哈夫曼树生成哈夫曼编码
5) 输出编码
6) 设字符集及频度如下表:
字符空格 A B C D E F G H I J K L M
频度186 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.3 进一步完成
1) 译码功能
2) 显示哈夫曼树
3) 界面设计的优化
2 内容
2.1 基本需求
编写一个哈夫曼编码/译码器,次编码/译码器有两大主要功能:一是对一段文本进行编码,比如在利用电报机发送信息时,需要将文字“ABACCDA”转换成类似“00110111001”这样的二进制组成的字符串;二是对一段密文进行译码,比如在接收电报后,需要对“0101110100101”这样的二进制密文通过某种标准译码成看得懂的文字信息。
另外还有一些辅助功能,比如可以打印一些简单的哈夫曼树的简图、有基
本的主菜单、简洁的操作界面、文件的读写。
2.2. 我的设计
哈夫曼编码/译码器主要有五个功能:初步编码、文件编码、手动译码、文件译码、退出。
初步编码:实现基本的编码,打印简单的哈夫曼树。输入N个字符和N个权值,输出每个字符对应的编码并打印哈夫曼树。注意此功能只是对哈夫曼编码的初探,只完成了生成哈夫曼树和哈夫曼编码的功能并没有实现文件的编码。
文件编码:对一个特定的文件进行编码,注意编码标准可以使用保存在data.txt中的默认标准也可以使用自己定义的标准。
手动译码:对手动输入的密文进行译码,译码标准可自定义或默认。
文件译码:对存放密文的文件进行译码,与手动译码的主要区别就是在密文的获取方法上。
退出:哈夫曼编码/译码器运行结束。
3 算法设计
3.1 数据的存储结构
3.1.1 存放哈夫曼树的存储结构:
typedef struct //存放树节点
{
char data; //节点代表的字符
int weight; //权值
int parent; //父节点
int lchild; //左孩子节点
int rchild; //右孩子节点
}HTNode;
3.1.2 存放哈夫曼编码的存储结构:
typedef struct //存放编码
{
char cd[60]; //
int start; //编码在数组中的开始下标
}HCode;
3.1.3 存放哈夫曼树每个节点位置的存储结构:
typedef struct //节点位置
{
char data;
int n;//在完全二叉树中的位置序号
}tree;
此存储结构的主要目的是记录哈弗曼树的每一个节点在完全二叉树上的位置序号,便于输出哈弗曼树的大致图形。
3.2 生成哈弗曼树的算法
算法思想:先将存有每个节点权值和数据的数组初始化,将每个节点的左右节点和父节点初始化为-1,保证每个节点都是独立的。假设有n 个节点,在建树时需要比较n-1次;在每一次的比较中,先找出两个权值最小的节点,将这两个节点作为孩子节点形成一个双亲节点并修改相应的属性值,形成的双亲节点的权值等于两孩子节点的和,双亲节点继续参加下一次的比较。最后得到的那个节点就是树的根节点。
算法流程图:
代码实现:
void CreateHT(int n,HTNode ht[60])//构造哈夫曼树
{
int i,k,lnode,rnode;
int min1,min2;
for(i=0;i<2*n-1;i++)
{
ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
}
for(i=n;i<2*n-1;i++)
{
min1=min2=32767;
lnode=rnode=-1;
for (k=0;k<=i-1; k++)
if (ht[k].parent==-1)
{
if (ht[k].weight { min2=min1;rnode=lnode; min1=ht[k].weight; lnode=k; } else if (ht[k].weight { 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; } } 3.3 生成哈弗曼编码的算法 算法思想:算法前提是哈弗曼树已经建立完成,假设有n个节点,这时每个节点都是叶子节点,只需从叶子节点向上找到根节点就可得到哈弗曼编码;此过程需要进行n次循环,在每次循环中顺着叶子节点向上遍历。如果它为左孩子 它所代表的编码数组增加一个字符1,右孩子则增加一个字符0,以此规律直到找到根节点,注意编码数组是从下标为n的位置开始依次向前存的。 算法流程图: 代码实现: void CreateHCode(int n,HTNode ht[60],HCode hcd[60])//得到哈弗曼编码{ int i,f,c; HCode hc; for(i=0; i { hc.start=n; 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++; hcd[i]=hc; } } 3.4 译码的算法 算法思想:算法前提是哈弗曼树已经建立完成,假设待译码的密文为“01010101001010”,这时就可以利用密文和哈弗曼树进行译码;密文从开头开始,哈弗曼树从根节点开始一一对应向前向下推进,如果密文为0则向该节点的右节点推进,为1则向左节点推进,当走到叶子节点时说明译码出了一个字符;之后再次返回根节点继续上一次的操作直到密文结束,注意密文必须连续且不重复使用。 算法流程图: 代码实现: void decipher(int n,HTNode ht[60],string code)//解码 { int i,j=0; i=2*n-2;//根节点的下标 cout<<"译码结果:\n"; int mi=1; for(int j=0;j { if(code[j]=='0') i=ht[i].lchild;//向下左 else if (code[j]=='1') i=ht[i].rchild;//向下右 else { mi = 0; break; } if(ht[i].lchild==-1) { cout< i=2*n-2; } } if(mi==0) cout<<"密文有误\n"; if(ht[i].lchild!=-1&&i!=2*n-2) cout<<"密文不完整\n"; } 3.5 打印哈弗曼树的算法 算法思想: 要想打印哈弗曼树的大致图形,首先要计算出哈弗曼树的每个节点在相应的二叉树中的序号,然后是控制节点间的空格和每行开始的空格。节点位置通过哈弗曼编码得到,位置f初始为1,对于每个节点从编码的第一个开始依次向后,如果为0 则f=f*2+1,为1则f=f*2,一直到编码结束即可算出最终位置。空格的解决方法是从最后一行到第一行节点间空格的数量分别是1、3、7…..2^i-1个,每行开头的空格分别是1、4、8……2^i个。 算法流程图: 3.6 其他算法 除了以上复杂的算法,还有一些简单的算法。比如限制输入的算法,只有当控制台输入某几个特定的字符时才会做出相应的反应;数据拆分算法,将从文件读入的数据进行拆分,把数字和非数字分别存入各自的数组里。 4 程序正确性验证 4.1 输入数据的控制 图1:主界面选择控制 图2:返回界面控制 图3:编码标准选择控制 运行解释:在主界面只有输入0-4中的数程序才会执行,返回界面也是如此;在选择Y或N时只有输入Y或N时程序才能。 4.2 打印哈弗曼树 图3 :哈弗曼树的简图 运行解释:我写的算法只有当节点较少时,才能看出哈弗曼树的大致图形。详情请见3.5。 4.3 哈弗曼编码 图4:哈弗曼编码 运行解释:当文件不存在时程序会提示重试,data.txt文件中存的是字符和字符的权值,也可以用自定义的编码标准,data3.txt中存的是待编码的字符。 4.4 哈弗曼译码 运行解释:如果要翻译的密文不完整,则会提示; 5 遇到的问题 这次的实验还算顺利,没有遇到太大的问题。刚开始对哈弗曼编码/译码的算法不是太了解,入手比较困难,但通过努力现在已经熟练掌握;再一个是文件读写的知识有点遗忘,浪费了点时间。我觉得打印哈弗曼树的那部分比较麻烦点,因为有些规律需要找,比如每行开头空几个空格才合适、节点间的空格是几个、节点什么时候输出才正确。 最后我要强调一个重要的点,在做大项目之前务必和老师讨论一下项目的需求,这样能更好的完成项目。 6 课程设计的主要收获 这次的实验收获很多,通过对哈弗曼编码/译码器的设计,我熟练掌握了哈弗曼编码和译码的算法实现,了解了哈弗曼编码译码的用途和现实意义; 同时对文件操作也有了较深的理解;感觉自己的编码能力有了进一步的提高,对数据结构有了更高的兴趣。 7 对今后课程设计的建议 1)在验收程序时,尽量体现学生的真实水平,提高验收标准。 2)在查重方面要进一步的加强,不要让偷懒的学生蒙骗过关。 3)文档的要求再详细一点。