《数据结构课程设计书》
课程题目 哈弗曼编码与译码
课程编号 J1620102
学生姓名 曾秋巧
所在专业 信息管理与信息系统
所在班级 信管1132班
任课老师 易学明
实习时间 2015.1.8
设计成绩
老师评语
目 录
第一章 问题描述.....................................................................................3
第二章 问题分析.....................................................................................3
第三章 逻辑结构和存储结构设计.........................................................3
第四章 算法分析.....................................................................................3
第五章 时间复杂度和空间复杂度分析..................................................4
第六章 源代码..........................................................................................4
第七章 程序运行结果..............................................................................7
第八章 心得..............................................................................................9
第一章、问题描述
在一个加密应用中,要处理的信息来自下面的字符集,各个字符的相关使用频度如下:
字符空格 A B C D E F G H I J K L M
频度 180 64 13 23 32103 22 15 47 57 1 5 31 20
字符 N O P Q R S T U V W X Y Z
频度 55 63 15 1 48 56 80 25 7 18 2 16 1
编写程序时实现如下功能:
(1)运行时,由用户输入来初始化字符集大小和相应用字符。
(2)输入一个要加密的字符串,将其加密。
(3)输出解密字符串。
第二章、问题分析
对字符集加密有多种方法,这里用的是哈弗曼编码对其进行加密。首先需要确定字符和权值,然后从权值中选择最小的两个构造根子树,以此类推,直到只剩下一棵树,便是哈弗曼树,将其左子树赋值为0,右子树赋值为1,对哈弗曼树进行遍历便可得到加密后的字符集。对字符集的解密则需要解读01串,0代表往左子树,1代表右子树,01串起到“路标”的作用,使之最后找到所属叶子即解密后的字符。
第三章、逻辑结构与存储结构设计
(1)逻辑结构设计:利用最优二叉树的叶子节点存储权值
(2)存储结构设计:利用链式存储结构存储,用链表表示一棵二叉树,即用链来表示逻辑关系。本文采用双亲孩子表示法,每个结点包含5个域,包括数据域,左右孩子,父母,权重。
第四章、算法分析
(1)从键盘输入结点值与权值
(2)给定n个带权的节点,如何构造一棵n个带有给定权值的叶节点的二叉树,使其带全路径长度WPL最小,构造最优树算法如下.
哈弗曼算法:
1. 将n个权值分别为w1,w2,w3,....wn-1,wn的节点按权值递增排序,将每个权值作为一棵二叉树构成n棵二叉树森林F={T1,T2,T3,T4,...Tn},其中每个二叉树都只有一个权值,其左右字数为空。
2. 在森林F中选取根节点权值最小二叉树,作为左右字数构成一棵新的二叉树,并使得新的二叉树的根节点为其左右字数权值之和,其中叶子都是最初的树。
3. 在森林F中删除这两棵树,同时将新得到的二叉树代替这两个树加入到森林F中,因此森林中二叉树的个数比以前少一颗。
4. 对新的森林重复2和3,直到森林中只有一棵树位置,这棵树就是哈夫曼树.
图一:
第五章、时间复杂度和空间复杂度分析
(1)时间复杂度:由于采取的是哈弗曼编码,构造哈弗曼树后,得到哈弗曼编码需要遍历每一个结点,在程序设计中共有26个结点,从根节点开始进行前序遍历,因此所花费的时间复杂度为O(n)。
(2)空间复杂度:空间复杂度是指程序运行所需要的内存空间,我们需要存放的有结点数据及关系,所以所需要内存空间为一个常数,所以空间复杂度为 O(1)。
第六章、源程序代码:
#include<iostream.h>
#include<stdio.h>
#include<malloc.h>
#define N 25 //树叶从0开始,最大为25
typedef struct //定义结构体
{
char data;//哈弗曼树结点值
int weight; //权重
int parent; //父母结点
int lchild; //左孩子
int rchild; //右孩子
} HtNode;
typedef struct//定义结构体
{
char cd[N];
int begin;
} HuffmanCode;
int main()
{
HtNode Ht[2*N]; //结点有2*n个
HuffmanCode hcd[N], d;
int i, k, f, l, r, n, c, s1, s2;
cout<<" * * * * * * * * * * * * * * * * * * \n"
<<" 哈弗曼编码与译码系统\n"
<<" * * * * * * * * * * * * * * * * * * \n";
cout<<"\n请输入要进行编码的元素个数:";
cin>>n;
cout<<"请输入各个元素的结点值与权值:\n";//从键盘输入结点值与权值
for(i=1;i<=n;i++)
{
cout<<" 第"<<i<<"个元素-->\n\t结点值:"; //对相应的点进行赋值
cin>>&Ht[i].data;
cout<<"\t权值:";
cin>>Ht[i].weight;
}
for(i=1;i<=2*n-1;i++)//初始化结点
Ht[i].parent=Ht[i].lchild=Ht[i].rchild=0;
for(i=n+1;i<=2*n-1;i++) //从所给的权值中找出最小的两个构造哈弗曼树
{
s1=s2=40000;
l=r=0;
for(k=1;k<=i-1;k++)
if(Ht[k].parent==0)
if(Ht[k].weight<s1)
{
s2=s1;
r=l;
s1=Ht[k].weight;
l=k;
}
else if(Ht[k].weight<s2)
{
s2=Ht[k].weight;
r=k;
}
Ht[l].parent=i; //设置两颗最小树的双亲
Ht[r].parent=i;
Ht[i].weight=Ht[l].weight+Ht[r].weight; //设置这棵3节点的树的根的权值以及孩子
Ht[i].lchild=l;
Ht[i].rchild=r;
}
for(i=1;i<=n;i++)//左子树为0,右子树为1
{
d.begin=n+1;
c=i; f=Ht[i].parent;
while(f!=0)
{
if(Ht[f].lchild==c)
d.cd[--d.begin]='0';
else
d.cd[--d.begin]='1';
c=f;
f=Ht[f].parent;
}
hcd[i]=d;
}
cout<<"编码后的字符代码为:\n"; //输出编码后的代码
for(i=1;i<=n;i++)
{
cout<<Ht[i].data<<": ";
for(k=hcd[i].begin;k<=n;k++)
cout<<hcd[i].cd[k];
cout<<"\n";
}
a:cout<<"\n* * *请选择操作:* * *\n";
cout<<" b:编码 y:译码 e:退出系统 ";
char hfm;
cin>>hfm;
if(hfm=='e')//退出系统
return 0;
else
{
switch(hfm)
{
case'b': //继续进行编码
{
int q ;
char bs;
cout<<"\n* * * 哈弗曼编码 * * *\n";
cout<<"请输入需要编码的字符: ";
for(q=0;bs!=10;q++)
{
bs=getchar();
for(i=1;i<=n;i++)
{
if (bs==Ht[i].data)
for(k=hcd[i].begin;k<=n;k++)
cout<<"编码后的字符代码为:"<<hcd[i].cd[k];
}
}
cout<<endl;
} break;
case'y': //进行译码(只能对已经编码的字符进行译码)
{
char e;
int t,u;
t=2*n-1;
cout<<"\n* * * 哈弗曼译码 * * *\n";
cout<<"\n* * * 请输入哈弗曼码(0/1): * *";
for(u=0;e!=10;u++)
{
if(Ht[t].lchild!=0)
{
e=getchar();
if(e=='0')
t=Ht[t].lchild;
else
t=Ht[t].rchild;
}
else
{
cout<<"输入的哈弗曼码的译码是:"<<Ht[t].data;
t=2*n-1;
}
}
cout<<endl;
} break;
} goto a; //返回到以a为标志的地方
} return 0;
}
第七章、程序运行结果
从键盘输入结点值与权值:加密后的哈弗曼编码及其译码:
第八章、心得
在课程设计过程中,我认真研究,根据课堂讲授内容,借助书本,自己动手实践。不但有助于消化课堂所讲解的内容,还可以增强独立思考能力和动手能力;通过编写实验代码和调试运行,我们可以逐步积累调试C++程序的经验并逐渐培养我们的编程能力、用计算机解决实际问题的能力。在实验过程中,我们不但有自己的独立思考,还借助各种参考文献来帮助我们完成系统。更为重要的是,同学之间加强了交流,在对问题的认识方面可以交换不同的意见。通过这次实验让我们明白:作为一名大学生必须严格训练分析总结能力、书面表达能力。需要逐步培养书写科学实验报告以及科技论文的能力。只有这样,我们的综合素质才会有好的提高。