哈夫曼树实验报告(付原C语言程序)
C语言-哈夫曼编码实验报告

福建工程学院课程设计课程:数据结构题目:哈夫曼编码和译码专业:信息管理信息系统班级: 1002班座号: 15号姓名:林左权2011年 6月 27日实验题目:哈夫曼编码和译码一、要解决的问题利用哈夫曼编码进行信息通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。
二、算法基本思想描述:根据给定的字符和其中每个字符的频度,构造哈夫馒树,并输出字符集中每个字符的哈夫曼编码.将给定的字符串根据其哈夫曼编码进行编码,并进行相应的译码.三、设计1. 数据结构的设计(1)哈夫曼树的表示设计哈夫曼树的结构体(htnode),其中包含权重、左右孩子、父母和要编码的字符。
用这个结构体(htnode)定义个哈夫曼数组(hfmt[])。
迷宫定义如下:typedef struct{int weight;int lchild;int rchild;int parent;char key;}htnode;typedef htnode hfmt[MAXLEN];(2)对原始字符进行编码初始化哈夫曼树(inithfmt)。
从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树。
并显示出每个字符的编码。
inithfmt(hfmt t)ey)hfmtpath(t,i,j);printf("\n");}(4)对用户输入的字符进行编码void decoding(hfmt t)child;if(t[j].lchild==-1){printf("%c",t[j].key);j=2*n-2;}}else if(r[i]=='1'){j=t[j].rchild;if(t[j].rchild==-1){printf("%c",t[j].key); j=2*n-2;}}}printf("\n\n");}四、源程序清单:#include <>#include <>#include <>#define MAXLEN 100typedef struct{int weight;int lchild;int rchild;int parent;char key;}htnode;typedef htnode hfmt[MAXLEN];int n;void inithfmt(hfmt t)eight=0;t[i].lchild=-1;t[i].rchild=-1;t[i].parent=-1;}printf("\n");}void inputweight(hfmt t)ey=k;printf("请输入第%d个字符的权值:",i+1); scanf("%d",&w);getchar();t[i].weight=w;printf("\n");}}void selectmin(hfmt t,int i,int *p1,int *p2)arent==-1)if(min1>t[j].weight){min1=t[j].weight;*p1=j;}for(j=0;j<=i;j++)arent==-1)if(min2>t[j].weight && j!=(*p1))eight;*p2=j;}}void creathfmt(hfmt t)arent=i;t[p2].parent=i;t[i].lchild=p1;t[i].rchild=p2;t[i].weight=t[p1].weight+t[p2].weight;}}void printhfmt(hfmt t)eight,t[i].parent,t[i].lchild,t[i].rchild,t[i].key); }printf("\n------------------------------------------------------------------\n");printf("\n\n");}void hfmtpath(hfmt t,int i,int j)arent;if(t[j].parent!=-1){i=j;hfmtpath(t,i,j);}if(t[b].lchild==a)printf("0");elseprintf("1");}void phfmnode(hfmt t)ey,t[i].weight);hfmtpath(t,i,j);}printf("\n------------------------------------------------------------------\n") ;}void encoding(hfmt t)ey)hfmtpath(t,i,j);printf("\n");}void decoding(hfmt t)child;if(t[j].lchild==-1){printf("%c",t[j].key);j=2*n-2;}}else if(r[i]=='1'){j=t[j].rchild;if(t[j].rchild==-1){printf("%c",t[j].key);j=2*n-2;}}}printf("\n\n");}int main(){int i,j;hfmt ht;char flag;printf(" |----------------------|\n"); printf(" |信管1002--林左权--15号|\n");printf(" |**********************|\n");printf(" | 哈夫曼编码课程设计 |\n");printf(" |**********************|\n");printf(" |设计完成时间:2011/6/27|\n");printf(" |----------------------|\n");creathfmt(ht);printhfmt(ht);phfmnode(ht);printf("\n------------------------------------------------------------------\n") ;printf("***************************编码&&译码&&退出***********************");printf("\n【1】编码\t【2】\t译码\t【0】退出");printf("\n您的选择:");flag=getchar();getchar();while(flag!='0'){if(flag=='1')encoding(ht);else if(flag=='2')decoding(ht);elseprintf("您的输入有误,请重新输入。
数据结构(C语言版)实验报告(哈夫曼树)

《数据结构与算法》实验报告一、需求分析1.问题描述:利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
对于双工通道(及可以双向传输信息的通道),每端都需要一个完整的编/译码系统。
试为这样的信息收发站写一个哈夫曼的编/译码系统。
2.基本要求一个完整的系统应具有以下功能:(1)I:初始化(Initialization)。
从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
(2)E:编码(Encoding)。
利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
(3)D:译码(Decoding)。
利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
(4)P:印代码文件(Print)。
将文件CodeFile以紧凑格式显示在终端上,每行50个代码。
同时将此字符形式的编码文件写入文件CodePrin中。
(5)T:印哈夫曼树(Tree printing)。
将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示出,同时将此字符形式的哈夫曼树写入文件TreePrint中。
3.测试数据(1)利用教科书例6-2中的数据调试程序。
(2)用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAM IS MY FAVORITE”。
4,实现提示(1)编码结果以文本方式存储在文件CodeFile中。
(2)用户界面可以设计为“菜单”方式:显示上述功能符号,再加上“Q”表示退出运行Quit。
请用户键入一个选择功能符。
此功能执行完毕后再显示此菜单,直至某次用户选择了“Q”为止。
(3)在程序的一次执行过程中,第一次执行I、D或C命令之后,哈夫曼树已经在内存了,不必再读入。
哈夫曼树的实验报告1

哈夫曼树的实验报告1一、需求分析1、本演示程序实现Haffman编/译码器的作用,目的是为信息收发站提供一个编/译系统,从而使信息收发站利用Haffman编码进行通讯,力求达到提高信道利用率,缩短时间,降低成本等目标。
系统要实现的两个基本功能就是:①对需要传送的数据预先编码;②对从接收端接收的数据进行译码;2、本演示程序需要在终端上读入n个字符(字符型)及其权值(整形),用于建立Huffman树,存储在文件hfmanTree.txt中;如果用户觉得不够清晰还可以打印以凹入表形式显示的Huffman树;3、本演示程序根据建好的Huffman树,对文件的文本进行编码,结果存入文件CodeFile中;然后利用建好的Huffman树将文件CodeFile中的代码进行译码,结果存入文件TextFile中;最后在屏幕上显示代码(每行50个),同时显示对CodeFile中代码翻译后的结果;4、本演示程序将综合使用C++和C语言;5、测试数据:(1)教材例6-2中数据:8个字符,概率分别是0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11,可将其的权值看为5,29,7,8,14,23,3,11(2)用下表给出的字符集和频度的实际统计数据建立Haffman树,并实现以下报文的编码和一、概要设计1、设定哈夫曼树的抽象数据类型定义ADT Huffmantree{数据对象:D={a i| a i∈Charset,i=1,2,3,……n,n≥0}数据关系:R1={< a i-1, a i >| a i-1, a i∈D, i=2,3,……n}基本操作:Initialization(&HT,&HC,w,n,ch)操作结果:根据n个字符及其它们的权值w[i],建立Huffman树HT,用字符数组ch[i]作为中间存储变量,最后字符编码存到HC中;Encodeing(n)操作结果:根据建好的Huffman树,对文件进行编码,编码结果存入到文件CodeFile 中Decodeing(HT,n)操作结果:根据已经编译好的包含n个字符的Huffman树HT,将文件的代码进行翻译,结果存入文件T extFile中} ADT Huffmantree1)主程序模块void main(){输入信息,初始化;选择需要的操作;生成Huffman树;执行对应的模块程序;输出结果;}2)编码模块——根据建成的Huffman树对文件进行编码;3)译码模块——根据相关的Huffman树对编码进行翻译;各模块的调用关系如图所示二、详细设计1、树类型定义typedef struct {unsigned int weight; //权值char ch1; //储存输入的字符unsigned int parent,lchild,rchild;}HTNode,*HuffmanTree;2、编码类型定义typedef char **HuffmanCode;哈夫曼编译器的基本操作设置如下Initialization(HuffmanTree &HT,HuffmanCode &HC,int *w,int &n,char *ch) //根据输入的n个字符及其它们的权值w[i],建立Huffman树HT,用字符数组ch[i]作为中间存储变量存储编码,最后转存到HC中;Encodeing(int n)//根据建好的包含n个字符的Huffman树,对文件进行编码,编码结果存入到文件CodeFile中Decodeing(HuffmanTree HT,int n)//根据已经编译好的包含n个字符的Huffman树HT,对文件的代码进行翻译,结果存入文件TextFile中基本操作操作的算法主函数及其他函数的算法void select(HuffmanTree HT,int n,int &s1,int &s2){ //依次比较,从哈夫曼树的中parent为0的节点中选择出两个权值最小的if(!HT[i].parent&&!HT[S1]&&!HT[S2]){if(HT[i].weight<ht[s1].weight){< p="">s2=s1; s1=i;}else if(HT[i].weight<ht[s2].weight&&i!=s1)< p=""> s2=i;}3、函数的调用关系图三、调试分析Encodeing Decoding Print PrintTreeInitialization1、本次实习作业最大的难点就是文件的读和写,这需要充分考虑到文件里面的格式,例如空格,换行等等,由于不熟悉C++语言和C语言的文件的输入和输出,给编程带来了很大的麻烦;2、原本计划将文本中的换行格式也进行编码,也由于设计函数比较复杂,而最终放弃;3、一开始考虑打印哈夫曼树的凹入表时是顺向思维,希望通过指针的顺序变迁来实现打印,但问题是从根结点到叶子结点的指针不是顺序存储的,所以未能成功,后来查找相关资料,最终利用递归的方法解决问题;4、程序中的数组均采用了动态分配的方法定义,力求达到减少空间的浪费;5、时间的复杂度主要是由查树这个步骤决定,因为无论是编码还是译码都需要对Huffman树进行查找和核对,但考虑到英文字母和空格也就是27个字符,影响不是很大;6、程序无论在屏幕显示还有文件存储方面都达到了不错的效果;7、程序不足的地方就是在文件文本格式方面处理得还是不够,或许可以通过模仿WORD的实现来改善。
哈夫曼树_实验报告

一、实验目的1. 理解哈夫曼树的概念及其在数据结构中的应用。
2. 掌握哈夫曼树的构建方法。
3. 学习哈夫曼编码的原理及其在数据压缩中的应用。
4. 提高编程能力,实现哈夫曼树和哈夫曼编码的相关功能。
二、实验原理哈夫曼树(Huffman Tree)是一种带权路径长度最短的二叉树,又称为最优二叉树。
其构建方法如下:1. 将所有待编码的字符按照其出现的频率排序,频率低的排在前面。
2. 选择两个频率最低的字符,构造一棵新的二叉树,这两个字符分别作为左右子节点。
3. 计算新二叉树的频率,将新二叉树插入到排序后的字符列表中。
4. 重复步骤2和3,直到只剩下一个节点,这个节点即为哈夫曼树的根节点。
哈夫曼编码是一种基于哈夫曼树的编码方法,其原理如下:1. 从哈夫曼树的根节点开始,向左子树走表示0,向右子树走表示1。
2. 每个叶子节点对应一个字符,记录从根节点到叶子节点的路径,即为该字符的哈夫曼编码。
三、实验内容1. 实现哈夫曼树的构建。
2. 实现哈夫曼编码和译码功能。
3. 测试实验结果。
四、实验步骤1. 创建一个字符数组,包含待编码的字符。
2. 创建一个数组,用于存储每个字符的频率。
3. 对字符和频率进行排序。
4. 构建哈夫曼树,根据排序后的字符和频率,按照哈夫曼树的构建方法,将字符和频率插入到哈夫曼树中。
5. 实现哈夫曼编码功能,遍历哈夫曼树,记录从根节点到叶子节点的路径,即为每个字符的哈夫曼编码。
6. 实现哈夫曼译码功能,根据哈夫曼编码,从根节点开始,按照0和1的路径,找到对应的叶子节点,即为解码后的字符。
7. 测试实验结果,验证哈夫曼编码和译码的正确性。
五、实验结果与分析1. 构建哈夫曼树根据实验数据,构建的哈夫曼树如下:```A/ \B C/ \ / \D E F G```其中,A、B、C、D、E、F、G分别代表待编码的字符。
2. 哈夫曼编码根据哈夫曼树,得到以下字符的哈夫曼编码:- A: 00- B: 01- C: 10- D: 11- E: 100- F: 101- G: 1103. 哈夫曼译码根据哈夫曼编码,对以下编码进行译码:- 00101110111译码结果为:BACGACG4. 实验结果分析通过实验,验证了哈夫曼树和哈夫曼编码的正确性。
哈夫曼树上机实验报告

霍夫曼树实验目的:掌握结构体、指针及二叉树的生成、遍历等操作掌握霍夫曼编码/译码的原理。
基本要求:熟练掌握树的操作。
程序实现:程序第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并把树的信息保存起来,以便解压时创建同样的哈夫曼树进行解压;第二遍,根据第一遍扫描得到的哈夫曼树进行编码,并把编码后的码字存储。
要点分析:题目中涉及的主要知识点:1、本程序参考霍夫曼算法(由给定的权值构造赫夫曼树):(1)由给定的n个权值{w0, w1, w2, …, wn-1},构造具有n棵二叉树的集合F ={T0, T1, T2, …, Tn-1},其中每一棵二叉树Ti只有一个带有权值wi的根结点,其左、右子树均为空。
(2)重复以下步骤, 直到F中仅剩下一棵树为止:①在F中选取两棵根结点的权值最小的二叉树, 做为左、右子树构造一棵新的二叉树。
置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
②在F中删去这两棵二叉树。
③把新的二叉树加入F。
2、用构造赫夫曼树以完成赫夫曼编码:把d1,d2,…, dn 作为叶子结点,把w1,w2,…,wn作为叶子结点的权,构造赫夫曼树。
在赫夫曼树中结点的左分支赋0,右分支赋1,从根结点到叶子结点的路径上的数字拼接起来就是这个叶子结点字符的编码。
3、译码的过程是分解电文中的字符串,从根出发,按字符‘0’或‘1’确定找左孩子或右孩子,直至叶子节点,便求得该子串相应的字符。
心得体会:通过本次实验,我熟练掌握了结构体、指针及二叉树的生成、遍历等操作,掌握了霍夫曼编码和译码的原理,熟练掌握树的操作,尤其是对霍夫曼树有了更深刻的理解。
同时,在编写代码的过程中方,对字符串的相关知识进行了回顾。
代码#include<stdio.h>#include<stdlib.h>#include<string.h>typedef struct{int weight;int parent,lchild,rchild;int sign;}HTNode,*HuffmanTree;typedef char * *HuffmanCode;void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n,char *s);void select(HuffmanTree &HT,int i,int &s1,int &s2);void creatHuffmanTree(int *w,char *s,char *r);void pr(HuffmanCode &HC,char r[],char s,char a[]);void HuffmanYM(HuffmanCode &HC,char r[],char a[],int n,HuffmanTree &HT);void HuffmanPass(HuffmanCode &HC,char r[],int n,HuffmanTree &HT);int main(){char s[100];char r[100];char a[100]="a";int w[100];int n,p;HuffmanTree HT;HuffmanCode HC;printf("请输入进行编码的字符串\n");scanf("%s",s);p=strlen(s);if(p!=1)creatHuffmanTree(w,s,r);printf("进行编码......\n");if(p!=1)HuffmanCoding(HT,HC,w,strlen(r)-1,r);else printf("%c的霍夫曼编码是: %c\n",s[0],'0');printf("霍夫曼码序列为:\n");if(p!=1)for(int i=0;i<strlen(s);i++)pr(HC,r,s[i],a);printf("\n");n=strlen(r)-1;if(p==1)printf("0\n");printf("霍夫曼编码进行译码:\n");if(p==1)printf("%c",s[0]);else HuffmanYM(HC,r,a,n,HT);printf("\n");printf("先序遍历输出叶子节点\n");if(p==1){printf("%c\n",s[0]);}else HuffmanPass(HC,r,n,HT);return 0;}void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int w[],int n,char s[]) {int s1,s2,f,c;int m,i,l;int start;char cd[101];if(n<1)return;l=strlen(s);m=2*n-1;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));HT[0].weight=10000;for (i=1;i<=n;++i){HT[i].weight=w[i-1];HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;HT[i].sign=0;}for(;i<=m+1;++i){HT[i].weight=0;HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;HT[i].sign=0;}for(i=n+1;i<=m;i++){select(HT,i-1,s1,s2);//选择最小的两个结点HT[s1].parent=i;HT[s2].parent=i;//将它们的父节点赋值HT[i].lchild=s1;HT[i].rchild=s2;HT[i].weight=HT[s1].weight+HT[s2].weight;}HC=(HuffmanCode)malloc((n+1)*sizeof(char *));cd[n-1]='\0';for(i=1;i<=n;i++){start=n;c=i;for(f=HT[i].parent;f!=0;f=HT[f].parent){if(HT[f].lchild==c){start--;cd[start]='0';}else{start--;cd[start]='1';}c=f;}HC[i]=(char *)malloc((n-start)*sizeof(char));for(int a=0;a<n-start;a++){HC[i][a]=cd[start+a];}HC[i][a]='\0';printf("%c的霍夫曼编码是: %s\n",s[i],HC[i]);}}void select(HuffmanTree &HT,int i,int &s1,int &s2){s1=0;s2=0;for(int j=1;j<=i;j++){if(HT[j].parent==0)if(HT[j].weight<=HT[s1].weight)s1=j;else continue;}else continue;}for(j=1;j<=i;j++){if(j==s1)continue;elseif(HT[j].parent==0){if(HT[j].weight<=HT[s2].weight)s2=j;else continue;}else continue;}}void creatHuffmanTree(int w[],char s[],char r[]) {int g=1;int q=0;r[0]='0';r[1]=s[0];w[0]=1;for(int e=1;e<strlen(s);e++){for(int k=1;k<=g;k++){if(r[k]==s[e]){w[k-1]++;q=1;}else continue;}if(q==0){r[++g]=s[e];w[g-1]=1;q=0;}r[++g]='\0';}void pr(HuffmanCode &HC,char r[],char s,char a[]){for(int i=1;i<strlen(r);i++){if(r[i]==s){printf("%s",HC[i]);strcat(a,HC[i]);}else continue;}}void HuffmanYM(HuffmanCode &HC,char r[],char a[],int n,HuffmanTree &HT) {int e=strlen(a);int k=0;int f=2*n-1;char b[10]="1";for(int j=1;j<=e;j++){if(HT[f].lchild!=0||HT[f].rchild!=0){b[k]=a[j];k++;if(a[j]=='1')f=HT[f].rchild;else if(a[j]=='0')f=HT[f].lchild;}else{for(int s=1;s<=n;s++){if(strcmp(HC[s],b)==0){printf("%c",r[s]);break;}else continue;}for(int u=0;u<10;u++)b[u]='\0';k=0;f=2*n-1;j=j-1;}}}void HuffmanPass(HuffmanCode &HC,char r[],int n,HuffmanTree &HT) {int f,k=0;char b[10]="a";f=2*n-1;HT[f].sign=0;if(HT[f].lchild==0&&HT[f].rchild==0)return;do{if(HT[f].lchild==0&&HT[f].rchild==0){for(int s=1;s<=n;s++){if(strcmp(HC[s],b)==0){printf("%c",r[s]);break;}else continue;}b[k--]='\0';HT[f].sign=2;f=HT[f].parent;}if(HT[f].sign==0){b[k]='0';HT[f].sign++;f=HT[f].lchild;k++;}elseif(HT[f].sign==1){b[k]='1';HT[f].sign++;f=HT[f].rchild;k++;}elseif(HT[f].sign==2){f=HT[f].parent;b[k--]='\0';}}while(f!=0);printf("%\n");}。
赫夫曼树实验报告(含源程序)

数据结构实验报告赫夫曼树一电文翻译设计目的本程序可将电文转换成由二进制的字符组成的字 符串,便于信息的传输。
用户可按照程序运行过 程中的提示,输入所要转换的电文,即可得到结 果。
设计原理程序中利用赫夫曼编码的原理设计赫夫曼树并存 储在一维数组中,从而得到能使电文总长最短的 二进制前缀编码。
利用得到的各字符的赫夫曼编 码既可对整段电文进行转换,也可对二进制电文 进行译码从而还原电文。
程序结构主函数:void main()注:主函数包含对所输入电文的字符统计功能,并将每个字符出现的次数作为其在整段电文中的权值。
1.2.3.求赫夫曼编码的函数:void HuffmanCoding()求无双亲结点中权值最小的两个结点的函数:void Select(int)将电文转换的函数:void translating(char [])译码函数:void yima()4. 算法概述由于在构成赫夫曼树之后,为求编码需从叶子结点出发走一条从叶子到根的路径;而为译码需从根出发走一条从根到叶子的路径。
则对每个结点而言,既需知双亲的信息,又需知孩子结点的信息(本程序只可将当次程序运行过程中所输入的电文译码)。
由此, 设定下述存储结构:typedef struct{unsigned int weight;unsigned int parent,lchild,rchild;}HTNode ,*HuffmanTree ; 〃动态分配数组存储赫夫曼typedef char **HuffmanCode; //动态分配数组存储赫夫曼编码表求赫夫曼编码的算法如下所示:void HuffmanCoding(HuffmanTree&HT,HuffmanCode &HC,int *w,int n){//w存放n个字符的权值(均>0)构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC oif(n<=1) return;m=2*n-1;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); //0 号单元未用for(p=&HT[1],i=1,k=0;i<=n&&k<n;++i,++p,++k) //*p={*w,0,0,0};for(;i<=m;++i,++p) //*p={0,0,0,0};for(i=n+1;i<=m;++i){ 〃建赫夫曼树〃在HT[1…i-1]选择parent为0且weight最小的两个结点,其序号分别为s1 和s2。
数据结构实验三哈夫曼树实验报告

题目:哈夫曼编/译码器一、题目要求:写一个哈夫曼码的编/译码系统,要求能对要传输的报文进行编码和解码。
构造哈夫曼树时,权值小的放左子树,权值大的放右子树,编码时右子树编码为1,左子树编码为0.二、概要设计:数据结构:typedef struct{int bit[MAXBIT];int start;} HCodeType; /* 编码结构体 */typedef struct{int weight;int parent;int lchild;int rchild;char value;} HNode; /* 结点结构体 */函数:void DEMONHuffmanTree (HNode HuffNode[MAXNODE], int n)作用:构造一个哈夫曼树,并循环构建int main ()作用:运用已经构建好的哈弗曼树,进行节点的处理,达到成功解码编译三、详细设计:哈夫曼树的建立:void DEMONHuffmanTree (HNode HuffNode[MAXNODE], int n){int i = 0, j, m1, m2, x1, x2;char x;/* 初始化存放哈夫曼树数组 HuffNode[] 中的结点 */while (i<n){HuffNode[i].weight = 0;//权值HuffNode[i].parent =-1;HuffNode[i].lchild =-1;HuffNode[i].rchild =-1;scanf("%c",&x);scanf("%c",&HuffNode[i].value); //实际值,可根据情况替换为字母i++;}/* 输入 n 个叶子结点的权值 */scanf("%c",&x);for(i=0;i<n;i++){scanf ("%d", &HuffNode[i].weight);}for (i=n; i<2*n-1; i++){HuffNode[i].weight = 0;//权值HuffNode[i].parent =-1;HuffNode[i].lchild =-1;HuffNode[i].rchild =-1;HuffNode[i].value=i;}/* 循环构造 Huffman 树 */for (i=0; i<n-1; i++){m1=m2=MAXQZ; // m1、m2中存放两个无父结点且结点权值最小的两个结点x1=x2=0;//找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 for (j=0; j<n+i; j++){if (HuffNode[j].weight < m1 && HuffNode[j].parent==-1){m2=m1;//m1中是最小x2=x1;m1=HuffNode[j].weight;x1=j;}else if (HuffNode[j].weight < m2 && HuffNode[j].parent==-1){m2=HuffNode[j].weight;x2=j;}} /* end for *//* 设置找到的两个子结点 x1、x2 的父结点信息 */HuffNode[x2].parent = n+i;HuffNode[n+i].weight = HuffNode[x1].weight + HuffNode[x2].weight; HuffNode[n+i].lchild = x1;HuffNode[n+i].rchild = x2;}}叶子节点的哈夫曼编码的保存:for (j=cd.start+1; j<n; j++)HuffCode[i].bit[j] = cd.bit[j];HuffCode[i].start = cd.start;主函数展示:int main(){HNode HuffNode[MAXNODE];HCodeType HuffCode[MAXLEAF],cd;int i, j, c, p, n,k=0;char wen[100];char z;scanf ("%d", &n);HuffmanTree (HuffNode, n);for (i=0; i < n; i++){cd.start = n-1;c = i;p = HuffNode[c].parent;while (p != -1) /* 父结点存在 */{if (HuffNode[p].lchild == c)cd.bit[cd.start] = 0;elsecd.bit[cd.start] = 1;cd.start--; /* 求编码的低一位 */c=p;p=HuffNode[c].parent; /* 设置下一循环条件 */} /* end while */for (j=cd.start+1; j<n; j++)HuffCode[i].bit[j] = cd.bit[j];} /* end for */z=getchar();z=getchar();for(;z!='\n';z=getchar()){wen[k++]=z;for(i=0;i<n;i++){if(z==HuffNode[i].value){for (j=HuffCode[i].start+1; j < n; j++)printf ("%d", HuffCode[i].bit[j]);break;}else;}}printf("\n");for(i=0;i<k;i++){printf("%c",wen[i]);}printf("\n");return 0;}四、调试分析与心得体会:虽然哈夫曼树的建立有书上的参考,但是实际写整个代码的时候还是问题重重。
哈夫曼树实验报告

哈夫曼树实验报告一、问题描述利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将穿来的数据进行译码,此试验即设计这样的一个简单的编/译码系统。
系统应该具有如下的几个功能。
1. 接受原始数据从终端任意读入字母,求出其各自的权重值,建立哈夫曼树,并将它存于hfmtree.dat文件中。
2. 编码利用已建好的哈夫曼树,对文件中的正文进行编码,然后将结果存入codefile.dat中。
3. 译码利用已建好的哈夫曼树将文件codefile.dat中的代码进行译码,结果存入文件textfile.dat中。
4. 打印编码规则即字符与编码的一一对应关系。
5. 打印哈夫曼树将已存在内存中的哈夫曼树以直观的方式显示在终端上。
二、数据结构设计1. 构造哈夫曼树时使用静态量表作为哈夫曼树的存储。
在构造哈夫曼树时,设计一个结构体数组HuffNode保存哈夫曼树中各节点的信息,根据二叉树的性质可知,具有n个叶子节点的哈夫曼树共有2n-1个结点,所以数组HuffNode的大小设置为2n-1,描述节点的数据类型为:typedef struct{int weight; //结点权值int parent;int lchild;int rchild;}HNodeType;2. 求哈夫曼编码时使用一位结构数组HuffCode作为哈腹满编码信息的存储。
求哈夫曼编码,实质上就是在以建立的哈夫曼树中,从叶子结点开始,沿结点的双亲链域退到根结点,每退回一步,就走过了哈夫满树的一个分支,从而得到一位哈夫曼码值,由于一个字符的哈夫曼编码是从根结点到相应叶子结点所经过的路径上各分支所组成的0、1序列,因此先得到的分支代码为所求编码的低位码,后得到的分支代码为所求编码的高位码,所以设计如下数据类型:typedef struct{int bit[26];int start;}HCodeType;3. 文件hfmtree.dat、codefile.dat、和textfile.dat。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
哈夫曼树实验报告需求分析:从终端读入一串字符,利用建立好的哈夫曼树对其进行编码,储存到文件当中去,然后从文件读入哈夫曼编码,针对每个字母对其进行译码,翻译为原来的信息。
二、概要设计程序分为以下几个模块:1、从终端读入字符集大小,n个字符和n个权值,建立哈夫曼树,写入文件hfmTree中去。
2、对hfmTree进行编码,建立hfm编码表。
3、从文件ToTran读入信息,根据hfm编码表对其进行hfm编码,将编码后的信息写入文件Codefile 中去4、对Codefile文件反向译码,结果储存在Textfile中去。
5、将建立的hfmTree打印在终端上,并储存于相应的Treeprint文件中去。
抽象的数据定义如下:哈夫曼树结构typedef struct //定义哈夫曼树的结构{int weight; //权值int parent; //双亲int lchild; //左孩子int rchild; //右孩子}htnode,huffmantree[M+1];建立哈夫曼树void crthuffmantree(huffmantree ht,int w[],int n) //初始化哈夫曼树{int i,s1,s2,m;for(i=1;i<=n;i++){ht[i].weight=w[i];ht[i].parent=0;ht[i].lchild=0;ht[i].rchild=0;}m=2*n-1;for(i=n+1;i<=m;i++){ht[i].weight=0;ht[i].parent=0;ht[i].lchild=0;ht[i].rchild=0;}for(i=n+1;i<=m;i++){select(ht,i-1,&s1,&s2);ht[i].weight=ht[s1].weight+ht[s2].weight;ht[s1].parent=i;ht[s2].parent=i;ht[i].lchild=s1;ht[i].rchild=s2;}}typedef char *huffmancode[N+1]; //建立哈夫曼树编码表void crthuffmancode(huffmantree ht,huffmancode hc,int n){char *cd; //新建立一个指针int start,i,c,p;cd=(char*)malloc(n*sizeof(char));//分配求一个字符的哈夫曼编码的空间cd[n-1]='\0'; //编码结束符for(i=1;i<=n;i++){start=n-1;c=i;p=ht[i].parent;while(p!=0){--start;if(ht[p].lchild==c)cd[start]='0';elsecd[start]='1';c=p;p=ht[p].parent;}hc[i]=(char *)malloc((n-start)*sizeof(char));strcpy(hc[i],&cd[start]);}free(cd);}select(huffmantree ht,int pos,int *s1,int *s2) //取最小和次小权值{int j,m1,m2;m1=m2=maxint;for(j=1;j<=pos;j++){if(ht[j].weight<m1&&ht[j].parent==0) //定义为双亲为零时才开始求,这样就做到了防止筛选过的重新加入比较列表里{m2=m1;*s2=*s1;*s1=j;m1=ht[j].weight;}else if(ht[j].weight<m2&&ht[j].parent==0){m2=ht[j].weight;*s2=j;}}}主程序模块int main(){初始化参数printf("请输入:初始化 I;编码 E;译码 D;印代码文件 P;印哈弗曼树 T\n");printf("结束请输入Q\n");while(1){ //这就是用户输入scanf("%c",&q);if(q=='Q'){break;}if(q=='I'){fpw=fopen("hfmtree.txt","w");ftest=fopen("date.txt","r");printf("请输入密码表,第一位表示密码表大小,之后按空格键开始以紧凑的格式输入编码字母和权值。
\n");scanf("%d",&n); //字符集大小输入getchar(); //吃空格用的for(i=1;i<=n;i++) //这里本来是要在终端输入的,我为了图个测试方便就写文件里,可以自己改回来{fscanf(ftest,"%c",&a);if(a!='\n'){str[i]=a;fscanf(ftest,"%d",&weight[i]);}else i--; //减去回车键的计数,应该是用于输入量过多情况下需要隔行输入的设定}for(i=n+1;i<=2*n-1;i++) //补全剩余的{str[i]='0';}crthuffmantree(ht1,weight,n); //建立哈夫曼树crthuffmancode(ht1,hc1,n); //建立哈夫曼编码表for(i=1;i<=2*n-1;i++) //打印到文件中去{fprintf(fpw,"%c %d %d %d %d\n",str[i],ht1[i].weight,ht1[i].parent,ht1[i].lchild,ht1 [i].rchild);}fclose(ftest); //关闭文件,这点很重要,否则文件不会写入fclose(fpw);}if(q=='E'){j=1;i=1;fpr=fopen("tobetran.txt","r");fpr2=fopen("hfmtree.txt","r");fpw2=fopen("codefile.txt","w");while(fscanf(fpr2,"%c %d %d %d %d\n",&str[j],&ht1[j].weight,&ht1[j].parent,&ht1[j]. lchild,&ht1[j].rchild)!=EOF){j++; //计数,哈夫曼树值大小}while(fscanf(fpr,"%c",&dst[i])!=EOF){i++; //所输入文件字符大小}count=j; //count代表就是哈夫曼树大小,count1代表输入文件大小count1=i;crthuffmancode(ht1,hc1,count-1); //重新从文件里建立哈夫曼编码表for(i=1;i<=count1-1;i++){for(j=1;j<=count/2;j++) //count/2就是字符集大小了{if(dst[i]==str[j]) //比较{fprintf(fpw2,"%s",hc1[j]); //找到后写入对应的编码到文件中去,也就是codefile那个文件}}}fclose(fpr);fclose(fpr2);fclose(fpw2);}if(q=='D'){j=1;i=1;z=1;fpr2=fopen("hfmtree.txt","r");fpr3=fopen("codefile.txt","r");fpw3=fopen("textfile.txt","w");while(fscanf(fpr3,"%c",&dst[i])!=EOF){i++;}count3=i;while(fscanf(fpr2,"%c %d %d %d %d\n",&str[j],&ht1[j].weight,&ht1[j].parent,&ht1[j].lchi ld,&ht1[j].rchild)!=EOF){j++;}count2=j; //count2是哈夫曼树大小计数,count3是读入的哈夫曼编码计数crthuffmancode(ht1,hc1,count2-1); //重新建立哈夫曼编码表for(i=1;i<count3;i++){for(j=1;j<=count2/2;j++){len=strlen(hc1[j]); //对每个字符哈夫曼编码长度统计for(t=0;t<len;t++){if(hc1[j][t]==dst[t+i]) //这里就相当字符匹配了{flag=1;}if(hc1[j][t]!=dst[t+i]){flag=0;break;}}if(flag==1&&t==len) //判断{win[z]=str[j]; //写入字符z++;i=i+len-1;break;}}}count4=z;for(z=1;z<count4;z++) //写入文件中{fprintf(fpw3,"%c",win[z]);}fclose(fpr2);fclose(fpr3);fclose(fpw3);}if(q=='P') //打印codefile文件{j=1;fpr2=fopen("codefile.txt","r");fpw3=fopen("codeprin.txt","w");while(fscanf(fpr2,"%c",&dst[j])!=EOF){j++;}count=j;j=0;for(i=1;i<count;i++){j++;if(j==50){printf("\n");j=0;}printf("%c",dst[i]);fprintf(fpw3,"%c",dst[i]);}fclose(fpr2);fclose(fpw3);}if(q=='T') //打印哈夫曼树,完备的树结构不好打印,这里就是通过本身结构大致打印出来{fpw=fopen("treeprin.txt","w");fpr=fopen("hfmtree.txt","r");i=1;m=0;while(fscanf(fpr,"%c %d %d %d %d\n",&str[i],&ht1[i].weight,&ht1[i].parent,&ht1[ i].lchild,&ht1[i].rchild)!=EOF){m++;i++;}n=(m+1)/2; //字符集大小maxlen=0; //最大字符编码长度crthuffmancode(ht1,hc1,n); //重新建立哈夫曼编码表for(i=1;i<=n;i++){len=strlen(hc1[i]);if(maxlen<len)maxlen=len;}count=maxlen; //求得最大字符编码长度,用来控制行数的for(i=1;i<=m;i++){t=0;if(ht1[i].parent==0) //先寻找根节点,打印出来{for(c=0;c<2*count;c++) //打印空格{printf(" ");fprintf(fpw," ");}printf("%d\n",ht1[i].weight);fprintf(fpw,"%d\n",ht1[i].weight);count=count-1; //跳入下一行j=ht1[i].lchild;low[t]=j; /记录根节点右子树,用数组low[]t++;k=ht1[i].rchild; //记录根节点左子树low[t]=k;t++;for(c=0;c<2*count;c++) //打空格{printf(" ");fprintf(fpw," ");}printf("%d ",ht1[j].weight);//打印根节点的右子树和左子树fprintf(fpw,"%d ",ht1[j].weight);printf(" ");fprintf(fpw," ");printf("%d\n",ht1[k].weight);fprintf(fpw,"%d\n",ht1[k].weight);count=count-1;}}p=0;while(p<maxlen-2) //p来控制打印的结束标志{for(j=0;j<2*count;j++){printf(" ");fprintf(fpw," ");}y=t; //y作为每一行节点数的记录用t=0;for(i=0;i<y;i++) //下面就是一个循环过程{if(ht1[low[i]].lchild!=0){printf("%d ",ht1[ht1[low[i]].lchild].weight);fprintf(fpw,"%d ",ht1[ht1[low[i]].lchild].weight);}else{printf("0 ");fprintf(fpw," ");}if(ht1[low[i]].rchild!=0){printf("%d ",ht1[ht1[low[i]].rchild].weight);fprintf(fpw,"%d ",ht1[ht1[low[i]].rchild].weight);}else{printf("0 ");fprintf(fpw," ");}if(ht1[low[i]].lchild!=0) //记录本层的孩子数量和位置,用数组d[] {d[t]=ht1[low[i]].lchild;t++;}if(ht1[low[i]].rchild!=0){d[t]=ht1[low[i]].rchild;t++;}}for(i=0;i<t;i++) //转移{low[i]=d[i];}printf("\n");p++;count=count-1;}fclose(fpw); //记住关文件fclose(fpr);}}return 0;}函数的调用关系MianCrthuffmancode crthuffmantree select设计和调试分析1、打印树本来是考虑用严格格式输出,但是需要空间太大,就用类似树的形式输出2、文件操作最后必须要关闭文件,否则会一直在缓冲区中3、注意关闭指针用户手册用户界面如下用户需要预先准备好totran文件,然后根据提示输入字符集大小和每个字母和对应的权值,然后按E编译代码,D印刷代码,P打印哈夫曼树到文件和终端上。