《数据结构课程设计报告赫夫曼编码.doc》由会员分享,可在线阅读,更多相关《数据结构课程设计报告赫夫曼编码.doc(26页珍藏版)》请在三一办公上搜索。
1、数据结构课程设计 题目:赫夫曼编码 院、 系:计算机科学与工程学院学科专业: 计算机科学与技术 学 生: 学 号: 指导教师: 2010年12月目 录1课程设计的题目- 0 页2课程设计的目的(设计要解决的问题)-2页3概要设计(函数划分、总体设计)- 2页4详细设计(各函数算法、流程图、程序)-3页5调试结果-23页6课程设计总结-24页7心得体会- 24页一、 课程设计的目的:1 了解利用赫夫曼树求解给定权值的字符串的赫夫曼编码的算法思想;2 学习利用计算机程序语言实现赫夫曼树构造的算法;3 利用构造好的赫夫曼树对任一字符串求解其赫夫曼编码,以此熟悉赫夫曼编码的整个求解过程;二、 概要设计
2、:(一) 函数划分:功能模块总共分为四个块,由四个主要的函数来实现,分别为:1 linklist calculation(char temp,int n)-统计字符种类及各个字符在字符串中出现次数的函数:用temp接收用户输入的字符串,将字符串存入一个链表当中,链表的每个结点存储字符串的一个字符,然后用两个指针p和t,查找字符串中相同的字符,将相同的字符结点删除,只保留一个,并且在保留的那个字符结点中统计当前字符的权值(即其在字符串中出现的次数),统计完成后将链表的头结点返回;2 status auto_calculation(linklist L)-自动统计权值的函数,以每个字符在字符串中出
3、现的次数作为其权值:将calculation()中统计的各个字符在字符串中出现的次数作为权值存储到全局数组w中;3 status input_weight(linklist L)-手动输入各个字符权值的函数:对之前建立好的存储字符串中每种字符信息的链表的结点进行操作,手动输入每个字符的权值,并将其对应的存储在每个结点的权值变量weight中,待链表中所有字符权值手动输入完毕后,再将链表中存储的每种字符的权值赋给存储权值的全局数组w中;4 void Creat_HuffmanTree(HuffmanTree &HT,linklist L)-根据字符串中每个字符的权值构造赫夫曼树的函数:利用之前用
4、于存储存储字符串中每种字符信息的链表,统计出字符种类个数n(即链表的结点数),然后开辟长度为2n的存储空间构造赫夫曼树,这其中还需要调用选择权值最小的两个结点的select()函数;5 void text_code(HuffmanTree HT,linklist head,int n)-根据构造好的赫夫曼树求解字符串中每个字符的编码的函数:将之前构造好的赫夫曼树,由叶子结点向根结点求每个字符的赫夫曼编码,并且将得到的编码保存与相应字符对应的一个数组中,这个数组是Code这个结构体的一个成员,它其中还包括存储每个字符及其权值的变量;6 void Calculte_code(char temp,l
5、inklist head)-输出每个字符以及整个字符串的赫夫曼编码的函数:将每个字符输出,同时逆序输出求解得到的编码,即是其对应的赫夫曼编码,然后输出整个字符串的赫夫曼编码;7 选择权值最小的两个结点的函数-void select(HuffmanTree &HT,int nn,int *s11,int *s22),这是辅助建造赫夫曼树的函数。(二) 总体设计: 求解一个字符串的赫夫曼编码的算法主要是根据字符串中各个字符的权值构造赫夫曼树来进行编码的,对于字符的权值,我采用了自动统计和手动输入两种方式得到,这样可以方便用户的各种需求。 对于自动统计各个字符的权值这一功能,主要是依据各个字符在字符
6、串中出现的次数作为它的权值。因为字符串中会有重复出现的字符,而它们的权值是每种字符出现的次数即可,即只需将统计得到的权值存储在一个字符的信息中即可,因此,我采用了链表这种数据结构,将用户输入的字符串的每个字符分别保存在一个链表的每个结点中,然后查找链表中字符相同的结点,用一个指针p从链表的第一个结点开始,再用一个查找指针t指向p的下一个结点,然后让指针t从p的下一个结点开始,查找p后面的所有结点,如果找到与p中的字符相同结点,就将t所指的结点从链表中删除,p所指的结点的权值加1,t继续向下查找,直至查找完最后一个结点,然后将p指向下一个待查找的字符,t继续从p的下一个结点开始,重复上述过程,直
7、至将链表中的所有结点都查找一遍,最后,链表中存储的就是字符串中不同的字符及它们各自在字符串中的出现次数。 对于手动输入各个字符权值这一功能模块,同样需要先统计出字符串中不同字符的个数,然后给每个字符手动赋上相应的权值,因此,同样利用统计各个字符在字符串中出现次数的链表,手动给每个结点中的字符赋权值,覆盖掉原先的出现次数即可。 待自动统计或者通过手动输入的方式给每个字符赋上相应的权值以后,将链表中的权值对应的存储到一个全局数组w中,w数组定义为全局变量的原因是整个程序运行过程中,多个函数都会访问它,因此定义为全局变量可以方便每个函数的访问。然后,统计出字符串中不同字符的个数n,开辟2n个连续的存
8、储单元来建立赫夫曼树,其中这段存储空间中的0号单元不使用,而作为后面判断每个结点是否存在双亲或者左右孩子使用。开始构造赫夫曼树,先将n个字符的权值对应存储到1n号存储单元中,并且将它们的左右孩子以及双亲都初始化为0号单元,即没有双亲和左右孩子,然后从现有的权值表中选出两个双亲为0且权值最小的相加后,添加到n+12n单元中,更新权值表,重复上述过程,建立赫夫曼树。赫夫曼树构造完成后,将此存储空间的头指针通过宏引带回,方便后续的操作。 调用一个text_code()函数对建立好的赫夫曼树有叶子结点向根结点逆向求每个叶子结点的编码,并将编码存储在一个结构体成员数组中,这个结构体还包含存储字符的变量,
9、以及存储字符权值的变量,这样就可以将每个字符与它的权值和编码对应起来。 最后,利用之前用于统计字符种类及其每个字符出现次数的链表倒序输出每个字符的编码,即得到它的赫夫曼编码,然后,在输出整个字符串的赫夫曼编码。三、 详细设计(一) 各函数算法:1 Linklist calculation(char temp,int n):字符数组temp接收用户输入的 字符串,整型变量n为字符串中字符的个数。若接收到的字符串不为空,则创建一个存储字符串的链表head,用一个for循环将字符串中的每个字符及其权值等相关信息存储在链表中的每个结点中,并且初始化每个字符的权值为1。链表建立完毕后,用一个指针p指向链
10、表的第一个结点,同时用一个指针t指向p的下一个结点,然后让指针t从p的下一个结点开始,查找p后面的所有结点,如果找到与p中的字符相同结点,就将t所指的结点从链表中删除,同时p所指的结点的权值加1,t继续向下查找,直至查找完最后一个结点,然后将p指向下一个待查找的字符,t继续从p的下一个结点开始,重复上述过程,直至将链表中的所有结点都查找一遍,最后,链表中存储的就是字符串中不同的字符及它们各自在字符串中的出现次数,以上操作完成后,将链表的头指针返回,算法结束;2 status auto_calculation(linklist L):L为之前建立好的链表的头结点指针,如果链表不为空,则用p指向头
11、结点的下一个结点,然后用一个for循环输出链表中各个结点中的字符及其对应的权值(这里为字符在字符串中的出现次数为其权值),同时用一个变量n统计出不同字符的个数,然后给一个指向整型变量的全局指针w开辟n个连续的存储整型数据的内存单元,将开辟好的存储空间的首地址返回给w,然后用一个指针p从链表的第一个结点开始,循环的将各个结点中的权值赋到w的每个存储单元中。如果链表为空,输出“统计错误”的提示信息。最后返回一个状态值;3 status input_weight(linklist L):L为之前建立好的链表的头结点指针,将指针p指向链表头结点的下一个结点(即第一个结点),如果链表不为空,p从第一个结
12、点开始直至最后一个,循环的得到用户给当前结点中字符赋的权值,同时用一个整型变量n统计链表的结点数,根据n的大小开辟一段存储整型数据的连续存储单元,并将此段存储空间的首地址返回给全局指针变量w,然后将链表中每个结点的权值赋到w这个数组的每个存储单元中。如果链表为空,则输出相应的提示信息,最后返回一个状态值;4 void Creat_HuffmanTree(HuffmanTree &HT,linklist L):L为之前建立好的链表的头结点的指针,HT用来返回构造好的赫夫曼树的头指针。首先,用一个指针temp_p指向L的下一个结点(即链表的第一个结点),然后用一个n统计出链表的结点数,即得到字符串
13、中不同字符的个数,如果nhead,head=p n=0 是 否 0=i 当it-a.code; 1=t-a.weight; t=p-next; NULL=t-next; 输出“没有字符” P=p-next p!=NULL p-next=t; p=p1; t!=NULL p-a.code=t-a.code 是 否 p-a.weight+ t-next=p-next t=p1; t=temp_p t-next=t; t-next=t 返回指针head的值2 status auto_calculation(linklist L):自动统计权值的函数,以每个字符在字符串中出现的次数为权值开始调用函数L
14、-next=pp=NULL 是 否 0=n; pNULL 输出字符及其对应的权值 n+;p-next=p; 输出“统计错误,没有字符!”用malloc开辟n个连续的整型 存储单元,首地址赋给w L-next=p; 0=i;当ia.weight=wi; 返回状态值OK; 返回状态值error函数调用完成3 status input_weight(linklist L):手动输入各个字符权值的函数开始调用函数L-next=ppNULL 是 否 0=n L-next=ppNULL 输入结点中每个字符的权值=p-a.weightn+p-next=p 输出“没有字符”的提示信息 用malloc开辟n个连
15、续的整型存储单元,并把首地址赋给wL-next=p0=i 当 ia.weight=wi返回状态值OK 返回状态值error函数调用完毕4 void select(HuffmanTree &HT,int nn,int *s11,int *s22):在构造赫夫曼树的顺序表中选出双亲为0,且权值最小的两个结点,并用s11和s22返回这两个结点所在顺序表中的序号开始调用函数HT=p1=k当knn Pk.parent=0 是 否 pk.weight=temp k=*s11 k+ break 1=k当knn pk.parent=0 是 否 pk.weighttemp k+ k=*s11 1=k当knn p
16、k.parent=0且k*s11是 否 pk.weight=temp k=*s22 k+ break 1=k当knnpk.weight=0且k*s11 是 否 pk.weighttemp k=*s22函数调用完毕5 void Creat_HuffmanTree(HuffmanTree &HT,linklist L):建立赫夫曼树的函数开始调用函数L-next=temp_p当temp_pNULLn+temp_p-next=temp_pn1 是 否 函数调用完毕 用malloc开辟n个连续HTnode 类型的存储单元,并把首地址赋给HT HT=p,1=i 当in时 wi-1=pi.weight;
17、0=pi.lchild 0=pi.rchlid 0=pi.parent i+ 当im时0=pi.weight;0=pi.parent;0=pi.lchild0=pi.rchild;i+n+1=iim0=S1;0=S2调用select(HT,i-1,&s1,&s2)i=HTs1.parenti=HTs2.parents1=HTi.lchildS2=HTi.rchildHTi.weight=HTs1.weight+HTs2.weighti+1=jjphead-next=tp1=i当in且tpNULLi=k1=j,pk.parent=mPm.lchild=k是 否 0=tp-a.cj-1 1=tp-
18、a.cj-1 当j-1tp-a.cj-1 i+ tp-next=tp 函数调用完毕7 void Calculte_Code(char temp,linklist head):输出各个字符及整个字符串编码的函数开始调用函数head-next=p当pNULL输出p-a.code0=n0=i当p-a.ci0n+i+n-1=i当i0输出p-a.cii-输出“电文的编码:”0=m0=k当tempk0m+k+0=i当inext=p当pNULL p-a.code=tempi 是 否 0=n;0=j 当p-a.cj0 n+ j+ n-1=j 当j0 输出p-a.cj j+ 输出“n”函数调用完毕(三) 源程序
19、:(由于采用了C+中的引用形参,因此必须采用.cpp为扩展名的源文件编译)/HuffmanCoding.cpp#include#include#include#define length 100;#define OK 1;#define error 0;typedef int status;typedef structunsigned int weight;unsigned int lchild ,rchild ,parent;HTnode,*HuffmanTree;typedef structchar code;unsigned int weight;char c10;Code,*Code_
20、p;typedef struct LnodeCode a;struct Lnode *next;Node,*linklist;typedef char *HuffmanCode;int *w; /w存放n个字符的相应权值linklist calculation(char temp,int n) /统计字符种类及各个字符在字符串中出现次数的函数,n表示字符串的个数int i=0;linklist p;linklist t;linklist head;/创建一个字符串链表head=(linklist)malloc(sizeof(Node); /创建一个带头结点的链表head-next=NULL;p
21、=head;if(n!=0)/将字符串存储到一个链表中,每个结点存储一个字符,方便后续统计字符的种类个数及其各个字符的权值for(i=0;ia.code=tempi;t-a.weight=1; /权值先初始化为1p-next=t;t-next=NULL;p=p-next;/*对链表进行处理,字符串中的每种字符只保留一个,同时统计每种字符在字符串中出现的次数,如果采用自动统计权值的话, 就以每个字符在字符串中出现的次数作为其权值*/linklist p1;p=head-next;linklist temp_p;while(p!=NULL)t=p-next;p1=p;while(t!=NULL)i
22、f(p-a.code=t-a.code)p-a.weight+;p1-next=t-next;temp_p=t;t=t-next;free(temp_p);elsep1=t;t=t-next;p=p-next;elseprintf(没有字符!);return head;status auto_calculation(linklist L) /自动统计权值的函数,以每个字符在字符串中出现的次数作为其权值printf(各字符权值统计如下:n);linklist p=L-next;if(p)int n=0;for(;p!=NULL;p=p-next)printf(%c : %d,p-a.code,p
23、-a.weight);printf(n);n+;w=(int *)malloc(n*sizeof(int);p=L-next;int i;for(i=0;inext)wi=p-a.weight;return OK;elseprintf(统计错误,没有字符!n);return error;status input_weight(linklist L) /手动输入各个字符权值的函数linklist p=L-next;if(p!=NULL)int n=0;printf(请输入各个字符的权值:n);for(p=L-next;p!=NULL;p=p-next)printf(%c:,p-a.code);s
24、canf(%d,&p-a.weight);n+;w=(int *)malloc(n*sizeof(int);p=L-next;int i=0;for(i=0;inext)wi=p-a.weight;return OK;elseprintf(没有字符!n);return error;void select(HuffmanTree &HT,int nn,int *s11,int *s22)int temp=0;HuffmanTree p=HT;int k;for(k=1;k=nn;k+)if(!pk.parent)temp=pk.weight;*s11=k;break;for(k=1;k=nn;k
25、+)if(!pk.parent)if(pk.weighttemp)temp=pk.weight;*s11=k;for(k=1;k=nn;k+)if(!pk.parent&k!=*s11)temp=pk.weight;*s22=k;break;for(k=1;k=nn;k+)if(!pk.parent&k!=*s11)if(pk.weight*s22)temp=*s11;*s11=*s22;*s22=temp;/建立赫夫曼树void Creat_HuffmanTree(HuffmanTree &HT,linklist L)int n=0;linklist temp_p=L-next;for(;t
26、emp_p!=NULL;temp_p=temp_p-next) /统计字符串中不同字符的个数n+;if(n=1)return;int m=2*n-1;int i;HuffmanTree p;HT=(HuffmanTree)malloc(m+1)*sizeof(HTnode); /零号单元未用for(p=HT,i=1;i=n;i+) /给它赋上相应的权值pi.weight=wi-1;pi.lchild=0;pi.rchild=0;pi.parent=0;for(;i=m;i+)pi.weight=0;pi.parent=0;pi.lchild=0;pi.rchild=0;int s1,s2; /建立huffman树for(i=n+1;i=m;i+)s1=0;s2=0;select(HT,i-1,&s1,&s2);HTs1.parent=i;HTs2.parent=i;HTi.lchild=s1;HTi.rchild=s2;HTi.weight=HTs1.weight+HTs2.weight;int j;for(j=1;jnext;int m;int k;in