课程设计报告课程设计题目:赫夫曼编码系统****:**专业:计算机科学与技术班级:1120702学号:****************:***2012年06 月20 日目录一、设计要求------------------------------------2二、存储结构------------------------------------2三、设计思想------------------------------------21、设计包含的几个部分-----------------------22、流程图-----------------------------------3四、详细设计------------------------------------4五、算法复杂度分析------------------------------8六、显示结果------------------------------------9七、心得体会------------------------------------11八、附录:源程序代码----------------------------11一、设计要求赫夫曼树任务:建立建立最优二叉树函数要求:可以建立函数输入二叉树,实现赫夫曼树的编码和译码系统,重复地显示并处理编码/解码功能,直到选择退出为止。
二、存储结构:在本次课程设计中,每一个字符的信息用一个结构体存储,包含结点值、权值、双亲结点、左孩子结点、右孩子结点等数据。
赫夫曼码和所有字符都是用一个一维数组建立存储的,所以本次课程设计的存储结构是顺序存储。
三、设计思想哈夫曼编译码系统的主要功能是先建立哈夫曼树,然后利用建好的哈夫曼树生成哈夫曼编码后进行译码。
在通信中可以采用0和1的不同排列来表示不同的字符,称为二进制编码。
而赫夫曼树在数据编码中的应用是数据的最小冗余编码问题他是数据压缩学的基础。
若每个字符出现的频率相同,则可以采用等长的二进制编码,频率不同,采用不等长的二进制编码,频率达的字符采用位数较少的编码,频率小的采用位数较多的编码。
赫夫曼编码就是一种不等长的二进制编码,而赫夫曼树是一种最优二叉树,它的编码也是一种最优编码。
在赫夫曼树中,规定往左编码为0,往右编码为1,则得到叶子节点的编码为从根结点带叶子结点中所有路径中0和1的顺序排列。
(1)设计包含的几个方面:①赫夫曼树的构造假设有n个权值,则构造出的赫夫曼树有n个叶子结点。
n个权值分别为w1,w2,………wn,则赫夫曼树构造规则为:1、将w1,w2,…….wn,看成有n棵树的森林。
2、在森林中选出两个根结点最小的树合并,作为一棵新树的左右子书,且新树根结点权值为左右子树根结点权值之和。
3、从森林中删除选取的两棵树,并将新树加入森林。
4、重复2和3步骤,直到森林中只剩一棵树为止。
②赫夫曼编码要求电文的赫夫曼编码,必须先定义赫夫曼编码类型,根据设计要求和实际需要定义的类型如下:typedet struct {char ch; // 存放编码的字符char bits[N+1]; // 存放编码位串int len; // 编码的长度}CodeNode; // 编码结构体类型③代码文件的译码在通信中,若将字符用赫夫曼编码形式发送出去,对方接收到编码后将编码还原成字符。
译码的基本思想是:读文件中编码,并与原先生成的赫夫曼编码表比较,遇到相等时,即取出其对应的字符存入一个新串中。
(2)其主要流程图如图所示。
四、详细设计(1)①赫夫曼树的存储结构描述为:#define N 50 // 叶子结点数#define M 2*N-1 // 赫夫曼树中结点总数typedef struct {int weight; // 叶子结点的权值int lchild, rchild, parent; // 左右孩子及双亲指针}HTNode; // 树中结点类型typedef HTNode HuffmanTree[M+1];②哈弗曼树的算法void CreateHT(HTNode ht[],int n) //调用输入的数组ht[],和节点数n{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; //所有结点的相关域置初值-1 for (i=n;i<2*n-1;i++) //构造哈夫曼树{min1=min2=32767; //int的范围是-32768—32767lnode=rnode=-1; //lnode和rnode记录最小权值的两个结点位置for (k=0;k<=i-1;k++){if (ht[k].parent==-1) //只在尚未构造二叉树的结点中查找{if (ht[k].weight<min1) //若权值小于最小的左节点的权值{min2=min1;rnode=lnode;min1=ht[k].weight;lnode=k;}else if (ht[k].weight<min2){min2=ht[k].weight;rnode=k;}}}ht[lnode].parent=i;ht[rnode].parent=i; //两个最小节点的父节点是iht[i].weight=ht[lnode].weight+ht[rnode].weight; //两个最小节点的父节点权值为两个最小节点权值之和ht[i].lchild=lnode;ht[i].rchild=rnode; //父节点的左节点和右节点}}(2)哈弗曼编码void CreateHCode(HTNode ht[],HCode hcd[],int n){int i,f,c;HCode hc;for (i=0;i<n;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++; //start指向哈夫曼编码hc.cd[]中最开始字符hcd[i]=hc;}}void DispHCode(HTNode ht[],HCode hcd[],int n) //输出哈夫曼编码的列表{int i,k;printf(" 输出哈夫曼编码:\n");for (i=0;i<n;i++) //输出data中的所有数据,即a-z printf(" %c:\t",ht[i].data);for (k=hcd[i].start;k<=n;k++) //输出所有data中数据的编码{printf("%c",hcd[i].cd[k]);}printf("\n");}}void editHCode(HTNode ht[],HCode hcd[],int n) //编码函数{char string[MAXSIZE];int i,j,k;scanf("%s",string); //把要进行编码的字符串存入string数组中printf("\n输出编码结果:\n");for (i=0;string[i]!='#';i++) //#为终止标志{for (j=0;j<n;j++){if(string[i]==ht[j].data) //循环查找与输入字符相同的编号,相同的就输出这个字符的编码{for (k=hcd[j].start;k<=n;k++){printf("%c",hcd[j].cd[k]);}break; //输出完成后跳出当前for循环}}}}(3)哈弗曼译码void deHCode(HTNode ht[],HCode hcd[],int n) //译码函数{char code[MAXSIZE];int i,j,l,k,m,x;scanf("%s",code); //把要进行译码的字符串存入code数组中while(code[0]!='#')for (i=0;i<n;i++){m=0; //m为想同编码个数的计数器for (k=hcd[i].start,j=0;k<=n;k++,j++) //j为记录所存储这个字符的编码个数{if(code[j]==hcd[i].cd[k]) //当有相同编码时m值加1m++;}if(m==j) //当输入的字符串与所存储的编码字符串个数相等时则输出这个的data数据{printf("%c",ht[i].data);for(x=0;code[x-1]!='#';x++) //把已经使用过的code数组里的字符串删除{code[x]=code[x+j];}}}}(4)主函数void main(){int n=26,i;char orz,back,flag=1;char str[]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}; //初始化int fnum[]={186,64,13,22,32,103,21,15,47,57,1,2,32,20,57,63,15,1,48,51,80,23,8,18,1,16}; //初始化HTNode ht[M]; //建立结构体HCode hcd[N]; //建立结构体for (i=0;i<n;i++) //把初始化的数据存入ht结构体中{ht[i].data=str[i];ht[i].weight=fnum[i];}while (flag) //菜单函数,当flag为0时跳出循环(5)显示部分源程序:{printf(" 欢迎使用赫夫曼编译系统\n");printf(" 制作人:章建\n");printf(" *************************************************\n");printf(" 1:显示编码\n ");printf(" 2:进行编码\n");printf(" 3:进行译码\n");printf(" 0:退出\n");printf(" *************************************************\n");printf(" 请输入选择的编号:");scanf("%c",&orz);switch(orz){case '1':system("cls"); //清屏函数CreateHT(ht,n);CreateHCode(ht,hcd,n);DispHCode(ht,hcd,n);printf("\n按任意键返回...");getch();system("cls");break;case '2':system("cls");printf("请输入要进行编码的字符串(以#结束,字符为小写英文字母):\n");CreateHT(ht,n);CreateHCode(ht,hcd,n);editHCode(ht,hcd,n);printf("\n按任意键返回...");getch();system("cls");break;case '3':system("cls");CreateHT(ht,n);CreateHCode(ht,hcd,n);DispHCode(ht,hcd,n);printf("请输入编码(以#结束):\n");deHCode(ht,hcd,n);printf("\n按任意键返回...");getch();system("cls");break;case '0':flag=0;printf(" 感谢您的使用!\n");break;default:system("cls");}}}五、算法复杂度分析:void editHCode(HTNode ht[],HCode hcd[],int n) //编码函数void deHCode(HTNode ht[],HCode hcd[],int n) //译码函数这两个被调函数里面都用了三重循环,其他的调用函数或者主函数都是一重或二重循环,所以算法复杂度为o(n^3)。