数据结构课程设计哈夫曼编码问题的设计和实现.docx
《数据结构课程设计哈夫曼编码问题的设计和实现.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计哈夫曼编码问题的设计和实现.docx(24页珍藏版)》请在冰豆网上搜索。
数据结构课程设计哈夫曼编码问题的设计和实现
数据结构课程设计--哈夫曼编码问题的设计和实现
成绩
课程设计说明书(论文)
题目哈夫曼编码问题的设计和实现
课程名称数据结构课程设计
院(系、部、中心)
专业
班级
学生姓名
学号
设计地点
指导教师
设计起止时间:
2008年6月2日至2008年6月6日
1问题描述
1.1题目内容
哈夫曼编码问题的设计和实现
输入一个英文字符串,对该字符串中各字符个数进行统计取得各字符的出现次数;以其出现次数作为关键字建立哈夫曼树并进行编码,最后输出各个字符对应的码值。
1.2基本要求
要求:
设计存储结构、基本算法(主要采用程序流程图体现);完成基本算法的实现代码;设计测试输入数据对程序进行测试,分析输出结果数据、算法的时间复杂度分析,如有改进算法则提出算法的改进方法。
1.3测试数据
测试数据三组:
AAAABBBCCD(判断连续的字符串是否可行)
AABBAABCDC(判断间段的字符串是否可行)
AAAABBBCCD(判断含空格的字符串是否可行)
2需求分析
2.1程序的基本功能
该程序大体上有两个功能:
1.输入任何一个字符串后,该程序能统计不同字符串的个数,并以不同字符串的个数作为权值。
2.已知不同字母的权值,以该权值作为叶结点,构造一棵带权路径最小的树,对该树从叶结点到根结点路径分支遍历,经过一个分支就得到一位夫曼编码值。
(规定哈夫曼树中的左分支为0,右分支为1,则从根结点到每个叶结点所经过的分支对应的0和1组成的序列便为该结点对应字符的编码)
2.2输入值、输出值以及输入输出形式
输入值:
AAAABBBCCD
输出值:
W=4C=0
W=3C=10
W=2C=111
W=1C=110
输入值:
AABBAABCDC
输出值:
W=4C=0
W=3C=10
W=2C=111
W=1C=110
输入值:
AAAABBBCCD
输出值:
W=4C=11
W=1C=010
W=3C=10
W=2C=00
W=1C=011
2.3各个模块的功能要求
1.统计模块
任意输入一个字符串,不论字母是否相联,字符串中是否含空格都能统计出不同字母的个数。
2.建立哈夫曼树模块
以统计的字符串个数作为权值,利用仿真存储结构,建立带权路径最小的树。
其中对结点的存储需要六个域,分别是weight域,flag域,parent域,leftChild域,rightChild域。
weight域是对权值的存放,flag域是一个标志域,flag=0时表示该结点尚未加入到哈夫曼树中,flag=1时表示该结点已加入到哈夫曼树中。
3.哈夫曼编码模块
从哈夫曼树的叶结点到根结点路径分支逐步遍历,每经过一个分支就得到一位哈夫曼编码。
哈夫曼编码也利用仿真存储结构。
4.主函数模块
从屏幕上输入字符串,调用函数,输出每个字母的权值与编码。
3概要设计
3.1所需的ADT,每个程序中使用的存储结构设计说明(如果指定存储结构请写出该存储结构的定义)
抽象数据类型集合:
在该程序中未用到抽象数据类型,主要用到的数据类型为:
int,char。
在哈夫曼树的建立与哈夫曼树的编码中用到仿真存储
3.2主程序流程以及模块调用关系
输入字符串——〉调用count函数——〉申请内存空间——〉调用Haffman
——〉调用HaffmanCode——〉输出权值与编码。
3.3各个模块的算法设计说明
1.主函数模块
n
y
y
n
n
y
主函数中利用gets输入一个字符串,调用count函数统计不同字母的个数,在调用
Haffman函数建立哈夫曼树,然后调用HaffmanCode函数进行编码,如果成功,输出权
值与编码,否则退出。
2.
统计函数
y
N
y
y
N
Y
统计函数在统计不同字符个数时先利用一个for循环遍历所有的字母,每遍历一个不同字母前令temp=1,然后用一个for循环将其后的字母与之比较,若相等则temp++,且将该字母从字符串中删除,否则从下一个字母遍历。
如此循环直到字符串结束。
3.Haffman函数
n
yn
n
y
n
y
n
y
y
Haffman函数主要以仿真结构存储信息,开始对每个域开始赋值,再根据不同的情况对每个域的值进行修改,如此循环下去,直到每个域的值修改完全。
4.HffmanCode函数
n
y
n
nny
y
HaffmanCode函数主要以仿真结构存储信息,由叶结点向根结点遍历,从数据域start域开始编码,bit数组存放编码,其编码为0,1序列.。
4详细设计
4.1数据类型
typedefstruct
{
intweight;/*权值*/
intflag;/*标记*/
intparent;/*双亲结点下标*/
intleftChild;/*左孩子下标*/
intrightChild;/*右孩子下标*/
}HaffNode;/*哈夫曼树的结点结构*/
typedefstruct
{
intbit[MaxN];/*数组*/
intstart;/*编码的起始下标*/
intweight;/*字符的权值*/
}Code;/*哈夫曼编码结构*/
intweight[16];/*用于存放权值*/
chars[30];/*存放字符串*/
函数调用
5各个算法实现的源程序
1.字符串统计源程序:
intcount(char*s,int*weight,intn)
{
inti,j,temp,k=0,p;
for(i=0;i='\0';i++)
{
temp=1;
for(j=0;j{
if(s[j]==s[i]&&i!
=j)
{
temp++;
for(p=j;ps[p]=s[p+1];
n--;
j--;
}
}
weight[k++]=temp;/*temp作为权值赋给weight数组*/
}
returni;/*返回权值个数*/
}
2.哈夫曼树建立源程序
voidHaffman(intweight[],intn,HaffNodehaffTree[])
/*建立叶结点个数为n,权值数组为weight的哈夫曼树haffTree*/
{
inti,j,m1,m2,x1,x2;
for(i=0;i<2*n-1;i++)
{
if(ihaffTree[i].weight=weight[i];
else
haffTree[i].weight=0;
haffTree[i].parent=0;
haffTree[i].flag=0;
haffTree[i].leftChild=-1;
haffTree[i].rightChild=-1;
}
/*构造哈夫曼树haffTree的n-1个非叶结点*/
for(i=0;i{
m1=m2=MaxValue;
x1=x2=0;
for(j=0;j{
if(haffTree[j].weight{
m2=m1;
x2=x1;
m1=haffTree[j].weight;
x1=j;
}
else
if(haffTree[j].weight{
m2=haffTree[j].weight;
x2=j;
}
}
/*将找出的两颗权值最小的子树合并为一棵子树*/
haffTree[x1].parent=n+i;
haffTree[x2].parent=n+i;
haffTree[x1].flag=1;
haffTree[x2].flag=1;
haffTree[n+i].weight=haffTree[x1].weight+haffTree[x2].weight;
haffTree[n+i].leftChild=x1;
haffTree[n+i].rightChild=x2;
}
}
3.哈夫曼树编码函数
voidHaffmanCode(HaffNodehaffTree[],intn,CodehaffCode[])
/*由n个结点的哈夫曼树haffTree构造哈夫曼编码haffCode*/
{
Code*cd=(Code*)malloc(sizeof(Code));
inti,j,child,parent;
/*求n个结点的哈夫曼编码*/
for(i=0;i{
cd->start=n-1;
cd->weight=haffTree[i].weight;
child=i;
parent=haffTree[child].parent;
/*由叶结点向上直到根结点*/
while(parent!
=0)
{
if(haffTree[parent].leftChild==child)
cd->bit[cd->start]=0;/*左孩子分支编码0*/
else
cd->bit[cd->start]=1;/*左孩子分支编码1*/
cd->start--;
child=parent;
parent=haffTree[child].parent;
}
for(j=cd->start+1;jhaffCode[i].bit[j]=cd->bit[j];/*保存每个叶结点的编码*/
haffCode[i].start=cd->start;/*保存不等长编码的起始位置*/
haffCode[i].weight=cd->weight;/*保存相应字符的权值*/
}
}
6调试分析
测试数据与输出结果与2.2结果相同。
对过函数的分析:
1.count函数:
最坏情况下时间复杂度为O(n*n*n),调试时必须给定字符串的长度,否则会输出乱码,这很不方便。
只要在条件语句中加上”s[i]!
='\0'”,就能解决。
同时对入含空格的字符串不能正确统计,因为输入字符串是以scanf的形式输入,scanf遇到空格自动结束,将scanf改为gets即可,出现gets(s);
2.Haffman函数在最坏情况下计算的次数为2*n-1+(3*n-3)*n/2时间复杂度最坏情况下为O(n*n);Haffman函数在书写时要注意定义变量的位置。
3.HaffmanCode函数在最坏情况下计算的次数为n*(3*n-1),时间复杂度为O(n*n);HaffmanCode函数在书写时要注意定义变量的位置。
4.主函数的时间复杂度又输入的字符串决定。
曾在主函数中将printf("输入:
\n");get(s),n=count(s,weight,30)放在Haffman(weight,n,myHaffTree);HaffmanCode(myHaffTree,n,myHaffCode)前面,出现很多问题,像“HaffNodeundefine”,”seeundeclearHaffNode”等,调整后没有问题。
主函数的时间复杂度由输入的字符串决定。
算法改进设想:
由于统计函数时间复杂度较高,是否降低其时间复杂度?
统计函数中用到三个循环,一个用于遍历所有字符,一个用于寻找相同字符,一个用于删除相同字符。
而且三个循环嵌套,如果能减少循环的次数或者是由较少的循环嵌套,就能降低时间复杂度了。
7使用说明
在写源程序前先将所要用到的函数都写好,以“haffman.h”保存起来。
再书写主函数,保存到相同的位置。
在vc环境下,用“Build”里面的“complie”,进行编译,运行。
利用Debug中的F5,F10,F9,F11设置断点等进行调试。
8测试结果
9源程序
包含哈夫曼树和图,以哈夫曼树为主要。
图在压缩文件中
哈夫曼树
#include
#include
#defineMaxValue10000
#defineMaxBit4
#defineMaxN10
typedefstruct
{
intweight;/*权值*/
intflag;/*标记*/
intparent;/*双亲结点下标*/
intleftChild;/*左孩子下标*/
intrightChild;/*右孩子下标*/
}HaffNode;/*哈夫曼树的结点结构*/
typedefstruct
{
intbit[MaxN];/*数组*/
intstart;/*编码的起始下标*/
intweight;/*字符的权值*/
}Code;/*哈夫曼编码结构*/
voidHaffman(intweight[],intn,HaffNodehaffTree[])
/*建立叶结点个数为n,权值数组为weight的哈夫曼树haffTree*/
{
inti,j,m1,m2,x1,x2;
for(i=0;i<2*n-1;i++)
{
if(ihaffTree[i].weight=weight[i];
else
haffTree[i].weight=0;
haffTree[i].parent=0;
haffTree[i].flag=0;
haffTree[i].leftChild=-1;
haffTree[i].rightChild=-1;
}
/*构造哈夫曼树haffTree的n-1个非叶结点*/
for(i=0;i{
m1=m2=MaxValue;
x1=x2=0;
for(j=0;j{
if(haffTree[j].weight{
m2=m1;
x2=x1;
m1=haffTree[j].weight;
x1=j;
}
else
if(haffTree[j].weight{
m2=haffTree[j].weight;
x2=j;
}
}
/*将找出的两颗权值最小的子树合并为一棵子树*/
haffTree[x1].parent=n+i;
haffTree[x2].parent=n+i;
haffTree[x1].flag=1;
haffTree[x2].flag=1;
haffTree[n+i].weight=haffTree[x1].weight+haffTree[x2].weight;
haffTree[n+i].leftChild=x1;
haffTree[n+i].rightChild=x2;
}
}
voidHaffmanCode(HaffNodehaffTree[],intn,CodehaffCode[])
/*由n个结点的哈夫曼树haffTree构造哈夫曼编码haffCode*/
{
Code*cd=(Code*)malloc(sizeof(Code));
inti,j,child,parent;
/*求n个结点的哈夫曼编码*/
for(i=0;i{
cd->start=n-1;
cd->weight=haffTree[i].weight;
child=i;
parent=haffTree[child].parent;
/*由叶结点向上直到根结点*/
while(parent!
=0)
{
if(haffTree[parent].leftChild==child)
cd->bit[cd->start]=0;/*左孩子分支编码0*/
else
cd->bit[cd->start]=1;/*左孩子分支编码1*/
cd->start--;
child=parent;
parent=haffTree[child].parent;
}
for(j=cd->start+1;jhaffCode[i].bit[j]=cd->bit[j];/*保存每个叶结点的编码*/
haffCode[i].start=cd->start;/*保存不等长编码的起始位置*/
haffCode[i].weight=cd->weight;/*保存相应字符的权值*/
}
}
intcount(char*s,int*weight,intn)
{
inti,j,temp,k=0,p;
for(i=0;i='\0';i++)
{
temp=1;
for(j=0;j{
if(s[j]==s[i]&&i!
=j)
{
temp++;
for(p=j;ps[p]=s[p+1];
n--;
j--;
}
}
weight[k++]=temp;
}
returni;
}
voidmain()
{
inti,j,n;
intweight[16];
chars[30];
HaffNode*myHaffTree;
Code*myHaffCode;
printf("************输入字符串************\n");
gets(s);
n=count(s,weight,30);
myHaffTree=(HaffNode*)malloc(sizeof(HaffNode)*(2*n+1));
myHaffCode=(Code*)malloc(sizeof(Code)*n);
if(n>MaxN)
{
printf("给出的n越界,修改MaxN!
\n");
exit
(1);
}
Haffman(weight,n,myHaffTree);
HaffmanCode(myHaffTree,n,myHaffCode);
/*输出每个叶结点的哈夫曼编码*/
for(i=0;i{
printf("W=%dC=",myHaffCode[i].weight);
for(j=myHaffCode[i].start+1;jprintf("%d",myHaffCode[i].bit[j]);
printf("\n");
}
}