井冈山大学电子与信息工程学院数据结构课程设计报告( 2012——2013年度第一学期)课程名称:数据结构课程设计题目一:6.3“哈夫曼树”的建立及其应用题目二: 3.4.6括号匹配的检验院系:计算机科学系班级:10计本(一)班姓名:刘晓倩学号:100911012指导教师:孙凌宇老师成绩:2012 年月日成绩评定一、指导教师评语二、成绩指导教师:日期:年月日设计题目<一>: 6.3“哈夫曼树”的建立及其应用一、设计要求1.问题描述设有一段电文由字符集{A,B,C,D,E,F,G,H}组成,各字符在电文中出现的次数集为{5,29,7,8,14,23,3,11},试设计各字符的哈夫曼编码。
2.需求分析(1)设计哈夫曼树。
具体的构造方法如下:以字符集{A,B,C,D,E,F,G,H}作为叶子结点,以各字符出现的次数{5,29,7,8,14,23,3,11}作为各叶子结点的权值构造一棵哈夫曼树。
(2)设计哈夫曼编码。
按照构造出来的哈夫曼树,规定哈夫曼树的左分支为0,右分支为1,则从根结点到每个叶子结点所经过的分支对应的0和1组成的序列便为该结点对应字符的哈夫曼编码。
二、概要设计1.主界面设计运行界面如图1所示:图1哈夫曼编码主菜单2.存储结构设计对于哈夫曼编码问题,希望在构造哈夫曼树的同时能方便地实现从双亲结点到左右孩子结点的操作,在进行哈夫曼编码时又要求能方便地实现从孩子结点到双亲结点的操作。
因此,本程序选择树的双亲孩子表示法作为哈夫曼树的存储结构,并加入了指示结点权值的信息。
3.系统功能设计本程序完成了从哈夫曼树的构造到实现并输出哈夫曼编码的过程,分别由两个子程序完成,其设计如下:(1)选择权值最小的树。
选择权值最小的树由函数Select()实现。
该功能按照哈夫曼树的构造步骤,在当前已构成的n(n>=2)棵二叉树的集合中选取两棵根结点权值最小的树作为左右子树构造一棵新的二叉树。
(2)哈夫曼编码。
哈夫曼编码由函数HuffmanCoding( )实现。
该功能首先调用函数Select()实现哈夫曼树的构造,然后从叶子到根逆向根据哈夫曼编码的要求,一次求出每个字符的哈夫曼编码。
三、模块设计1.模块设计本程序包含3个模块:主程序模块、哈夫曼编码模块和选择模块。
其调用关系如图2所示。
图2 模块调用示意图2.系统子程序及功能设计本程序共设置3个子程序,各子程序的函数名及功能说明如下。
(1)void Select(HuffmanTree &HT,int m,int *s1,int *s2)//选择权值最小的两个结点(2)void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n)//构造哈夫曼编码(3)void main( ) //主函数。
输入结点个数及权值,调用哈夫曼编码模块函数3.函数主要调用关系图本程序3个子程序之间的主要调用关系如图3所示。
图中数字是各函数的编号图3系统函数调用关系图四、详细设计1.数据类型定义typedef struct{unsigned int weight; //用来存放各个结点的权值unsigned int parent, lchild, rchild; //指向双亲、孩子结点的指针}HTNode, *HuffmanTree; //动态分配数组存储哈夫曼树typedef char * * HuffmanCode; //动态分配数组存储哈夫曼编码表2.系统主要子程序详细设计哈夫曼编码模块设计分两步:首先构造哈夫曼树,然后完成哈夫曼编码。
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n){//w存放n个字符的权值(均>0),构造哈夫曼树HT并求出n个字符的哈夫曼编码HC int i,j,m,s1,s2,start;char *cd;unsigned int c,f;if (n<=1) return;m = 2 * n - 1;HT = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); //0号单元未用for (i=1;i<=n;i++) //叶子结点初始化并放入1-n号单元{HT[i].weight=w[i];HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}for (i=n+1; i<=m;i++) //非叶子结点初始化{HT[i].weight=0;HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}printf("\n哈夫曼树的构造过程如下所示:\n");printf("HT初态:\n 结点weight parent lchild rchild");for (i=1;i<=m;i++) //完成构造哈夫曼树算法的第1个步骤printf("\n%4d%8d%8d%8d%8d",i,HT[i].weight,HT[i].parent,HT[i].lchild, HT[i].rchild);printf(" 按任意键,继续...");getch();//创建哈夫曼树HTfor (i=n+1;i<=m;i++){Select (HT,i-1,&s1,&s2);//在HT[1..i-1]中选择parent为0且weight最小的两个结点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;//置新二叉树的根结点权值为其左、右子树根结点之和printf("\nselect:s1=%d s2=%d\n",s1,s2);//根结点权值最小的树在HT中的位置printf(" 结点weight parent lchild rchild");for (j=1;j<=i;j++)//输出选取根结点权值最小树的过程printf("\n%4d%8d%8d%8d%8d",j,HT[j].weight,HT[j].parent,HT[j].lchild,HT[j].rchild);printf(" 按任意键,继续...");getch();}printf("\n%d个字符的哈夫曼编码如下:\n",n);//从叶子到根逆向求每个字符的哈夫曼编码HC=(HuffmanCode)malloc((n+1)*sizeof(char *));//分配n个编码的头指针cd = (char * )malloc(n*sizeof(char)); //分配求编码的工作空间cd[n-1] = '\0'; //编码结束符for (i=1;i<=n;i++) //逐个字符求哈夫曼编码{start =n-1; //编码结束符位置for (c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//叶子结点到根逆向求编码if (HT[f].lchild==c) cd[--start] ='0';else cd[--start] = '1';HC[i] = (char *)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间strcpy(HC[i], &cd[start]); //从cd复制编码(串)到HC }free(cd); //释放工作空间for(i=1;i<=n;i++)printf("<%2d>编码:%s\n",HT[i].weight,HC[i]);}//HuffmanCoding五、测试分析根据设计要求中的问题描述分别输入字符的个数和对应的权值,程序运行得到图4的开始界面。
图4哈夫曼编码程序开始界面构造哈夫曼树的过程如图(5~ 12)所示。
图5图6图7图8图9图10图11图12构造哈夫曼编码如图13所示。
图13 哈夫曼编码六、用户手册(1)本程序执行文件为“哈夫曼树的建立及其应用.exe”。
(2)进入本程序之后,分别输入哈夫曼编码字符的个数和对应的权值。
(3)随即显示哈夫曼树的构造过程,最终显示出对应权值的哈夫曼编码。
七、调试报告此次课程设计主要是了解哈夫曼树的设计,并学会哈夫曼编码的设计。
通过这次课程设计,我学到了课本上以外的许多知识,学会了树相关的基础知识,受益匪浅。
《数据结构》是一门实践性较强的课程,为了学好这门课程,必须在掌握理论知识的同时,加强上机实践。
其次是,根据实际问题的需要,对各个方面的优缺点加以综合平衡,从中选择比较适宜的实现方法。
比如在选择数据结构的时候,就要求从时间性能和空间性能去考虑,从而使得能编写出更加实用和高效的代码,而要做到这点,就要求对知识点很熟悉。
在这次课程设计中曾遇到了不少问题,比如输入整型变量的时候,没有办法限制其不能输入字符型,还有类型必须前后匹配等等。
使我明白了理论与实际相结合的重要性,使我更深刻地意识到:掌握了好的理论并不一定能马上将其运用到自己的程序中,而只有通过不断地学习和实践,不断地运用它,才能熟能生巧!八、程序清单#include <stdio.h>#include <malloc.h>#include <string.h>#include <conio.h>typedef struct{unsigned int weight; //用来存放各个结点的权值unsigned int parent,lchild,rchild; //指向双亲、孩子结点的指针}HTNode, *HuffmanTree; //动态分配数组存储哈夫曼树typedef char * * HuffmanCode; //动态分配数组存储哈夫曼编码表//1.选择权值最小的两个结点void Select(HuffmanTree &HT,int m,int *s1,int *s2){int i,min;for(i=1;i<=m;i++) //在HT[1..i-1]中选择parent为0且weight最小的两个结点{if(HT[i].parent==0){min = i;i=m+1;}}for(i=1;i<=m;i++) //parent为0且weight最小的两个结点,第一个序号为s1 {if(HT[i].parent == 0){if(HT[i].weight < HT[min].weight)min = i;}}*s1 = min;for(i=1; i<=m;i++) //在HT[1..i-1]中选择parent为0且weight最小的两个结点{if(HT[i].parent == 0 &&i!=(*s1)){min = i;i = m+1;}}for(i=1;i<=m;i++) //parent为0且weight最小的两个结点,第二个序号为s2 {if(HT[i].parent == 0 &&i!=(*s1)){if(HT[i].weight < HT[min].weight)min = i;}}*s2 = min;}//Select//2.构造哈夫曼编码void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n){//w存放n个字符的权值(均>0),构造哈夫曼树HT并求出n个字符的哈夫曼编码HC int i,j,m,s1,s2,start;char *cd;unsigned int c,f;if (n<=1) return;m = 2 * n - 1;HT = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); //0号单元未用for (i=1;i<=n;i++) //叶子结点初始化并放入1-n号单元{HT[i].weight=w[i];HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}for (i=n+1; i<=m;i++) //非叶子结点初始化{HT[i].weight=0;HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}printf("\n哈夫曼树的构造过程如下所示:\n");printf("HT初态:\n 结点weight parent lchild rchild");for (i=1;i<=m;i++) //完成构造哈夫曼树算法的第1个步骤printf("\n%4d%8d%8d%8d%8d",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild);printf(" 按任意键,继续...");getch();//创建哈夫曼树HTfor (i=n+1;i<=m;i++){Select (HT,i-1,&s1,&s2);//在HT[1..i-1]中选择parent为0且weight最小的两个结点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;//置新二叉树的根结点权值为其左、右子树根结点之和printf("\nselect:s1=%d s2=%d\n",s1,s2);//根结点权值最小的树在HT中的位置printf(" 结点weight parent lchild rchild");for (j=1;j<=i;j++)//输出选取根结点权值最小树的过程printf("\n%4d%8d%8d%8d%8d",j,HT[j].weight,HT[j].parent,HT [j].lchild,HT[j].rchild);printf(" 按任意键,继续...");getch();}printf("\n%d个字符的哈夫曼编码如下:\n",n);//从叶子到根逆向求每个字符的哈夫曼编码HC=(HuffmanCode)malloc((n+1)*sizeof(char *));//分配n个编码的头指针cd = (char * )malloc(n*sizeof(char)); //分配求编码的工作空间cd[n-1] = '\0'; //编码结束符for (i=1;i<=n;i++) //逐个字符求哈夫曼编码{start =n-1; //编码结束符位置for (c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//叶子结点到根逆向求编码if (HT[f].lchild==c) cd[--start] ='0';else cd[--start] = '1';HC[i] = (char *)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间strcpy(HC[i], &cd[start]); //从cd复制编码(串)到HC }free(cd); //释放工作空间for(i=1;i<=n;i++)printf("<%2d>编码:%s\n",HT[i].weight,HC[i]);}//HuffmanCoding//3.主函数void main(){HuffmanTree myHuffmanTree;HuffmanCode myHuffmanCode;int *w,i;int n, wei; //编码个数及权值printf("请输入需要哈夫曼编码的字符个数:");scanf("%d",&n);w=(int *)malloc((n+1) *sizeof(int));for(i=1;i<=n;i++){printf("请输入第%d字符的权值:",i);fflush(stdin);scanf("%d",&wei);w[i]=wei;}HuffmanCoding(myHuffmanTree,myHuffmanCode,w,n);}设计题目<二>: 3.4.6括号匹配的检验一、设计要求1.问题描述假设表达式中允许有两种括号:圆括号和方括号,其嵌套的顺序随意,即(()[ ])或[([ ] [ ])]等为正确格式,[( ])或((( ]均为不正确的格式。