目录一、程序设计目的与要求 (3)1.1程序设计目的 (3)1.2程序设计要求 (3)二、需求分析 (4)三、概要设计 (4)3.1哈夫曼树的构造过程 (4)3.2译码过程是编码过程的逆过程 (5)3.3 构造哈夫曼树和哈夫曼编码类的描述 (5)四、详细设计 (6)五、调试分析 (11)5.1程序编译界面 (11)5.2程序运行界面 (12)六、测试结果 (13)七、附录 (15)7.1设计心得 (15)7.2参考文献 (15)一、程序设计的目的与要求1.1程序设计目的课程设计是《数据结构》课程教学必不可缺的一个重要环节,通过课程设计,使学生对整个课程的知识体系有较深入的理解,在运用本课程的知识解决实际问题方面得到锻炼,对锻炼学生的实践能力以及运用本课程的知识、方法解决更为复杂的实际问题有较好的启发和指导作用,从而为后续课程的学习,毕业设计环节以及将来的实际工作打好坚实的基础。
本课程设计的目是:1.培养学生将所学的算法知识应用于程序设计过程中,设计出运行效率更高的程序;2.了解数据的三种逻辑结构(线性结构、树结构、图结构)和四种存储结构(顺序、链接、索引、散列)的基本特性和相互关系;3.掌握算法知识,学会设计算法并对算法进行分析和评价。
1.2程序设计要求在设计时严格按照题意独立进行设计,不得随意更改。
要求熟悉C、C++等某一种高级程序设计语言。
通过本课程的学习与实践,学生应做到:1.掌握数据结构的基本概念和基本理论。
2.熟练掌握顺序表、链表、队列、栈、树以及二叉树、图等基本数据结构的设计和分析。
3.熟练地掌握常用算法(递归、遍历、查找、排序)的知识。
4.能对所求解的问题进行分析,抽象出逻辑结构,选择合适的存储结构,定义所需的运算,设计相应的算法。
5.对算法进行分析和评价。
二、需求分析在当今信息爆炸时代,如何采用有效的数据压缩技术节省数据文件的存储空间和计算机网络的传送时间已越来越引起人们的重视,哈夫曼编码正是一种应用广泛且非常有效的数据压缩技术。
哈夫曼编码是一种编码方式,以哈夫曼树—即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。
哈弗曼编码使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。
这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数据的目的)。
哈夫曼编码的应用很广泛,利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。
树中从根到每个叶子都有一条路径,对路径上的各分支约定:指向左子树的分支表示“0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为和各个叶子对应的字符的编码,这就是哈夫曼编码。
哈弗曼译码输入字符串可以把它编译成二进制代码,输入二进制代码时可以编译成字符串。
三、概要设计3.1哈夫曼树的构造过程:用电文中各个字符使用的频度作为叶结点的权,构造一颗具有最小带权路径长度的哈夫曼树,若对树中的每个左分支赋予标记0右标记赋予1,则从根结点到每个叶结点的路径上的标记连接起来就构成一个二进制串,该二进制被称为哈夫曼编码。
3.2译码过程是编码过程的逆过程:从哈夫曼树的根开始,从左到右把二进制编码的每一位进行判别,若遇到0,则选择左分支走向下一个结点;若遇到1,则选择右分支走向下一个结点,直至到达一个树叶结点,便求得相应字符。
3.3构造哈夫曼树和哈夫曼编码类的描述:在构造哈夫曼树时要能方便地实现从双亲结点到左、右孩子结点的操作,而在进行哈夫曼树编码时又要求能方便地从结点到双亲结点的操作,因此,需要将哈夫曼树的结点存储结构设计为三叉链式存储结构。
此外,每一个结点还要设置全值域。
为了判断一个结点是否已加入到哈夫曼树中,每一个结点还要设置一个标志域flag,当flag=0时,表示该结点尚未加入到哈夫曼树中;当flag=1时,表示该结点已加入到哈夫曼树中。
这样,每一个结点应包含五个域,其存储结构示意图如图3.1 1所示。
weight flag parent rchild lchild图3.1 1哈夫曼树的结点存储结构示意图其中,weight域存放结点的权值;flag域存放结点是否加入哈夫曼树的标志值,等于1时表示已加入,否则没加入;parent、rchild、lchild域分别存放父结点,左、右孩子结点的地址。
四、详细设计#include <stdio.h>#include <stdlib.h>/*哈夫曼树建立、哈夫曼编码算法的实现所需头文件*/ #include <string.h>typedef char* HuffmanCode;/*动态分配数组,存储哈夫曼编码*/typedef struct{unsigned int weight ; /* 用来存放各个结点的权值*/unsigned int parent, LChild,RChild ; /*指向双亲、孩子结点的指针*/}HTNode, * HuffmanTree; /*动态分配数组,存储哈夫曼树*/void select(HuffmanTree *ht,int n, int *s1, int *s2){int i;int min;for(i=1; i<=n; i++){if((*ht)[i].parent == 0){min = i;i = n+1;}}for(i=1; i<=n; i++){if((*ht)[i].parent == 0){if((*ht)[i].weight < (*ht)[min].weight)min = i;}}*s1 = min;for(i=1; i<=n; i++){if((*ht)[i].parent == 0 && i!=(*s1)){min = i;i = n+1;}}for(i=1; i<=n; i++){if((*ht)[i].parent == 0 && i!=(*s1)){if((*ht)[i].weight < (*ht)[min].weight)min = i;}}*s2 = min;}void CrtHuffmanTree(HuffmanTree *ht , int *w, int n){ /* w存放已知的n个权值,构造哈夫曼树ht */int m,i;int s1,s2;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].LChild = 0;(*ht)[i].parent = 0;(*ht)[i].RChild = 0;}for(i=n+1;i<=m;i++){(*ht)[i].weight = 0;(*ht)[i].LChild = 0;(*ht)[i].parent = 0;(*ht)[i].RChild = 0;} /*非叶子结点初始化*//* ------------初始化完毕!对应算法步骤1---------*/for(i=n+1;i<=m;i++) /*创建非叶子结点,建哈夫曼树*/{ /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2返回*/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;}}/*哈夫曼树建立完毕*/void outputHuffman(HuffmanTree HT, int m){if(m!=0){printf("%d ", HT[m].weight);outputHuffman(HT,HT[m].LChild);outputHuffman(HT,HT[m].RChild);}void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)/*从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码*/{char *cd;int i;unsigned int c;int start;int p;hc=(HuffmanCode *)malloc((n+1)*sizeof(char *)); /*分配n个编码的头指针*/ cd=(char * )malloc(n * sizeof(char )); /*分配求当前编码的工作空间*/cd[n-1]='\0'; /*从右向左逐位存放编码,首先存放编码结束符*/for(i=1;i<=n;i++) /*求n个叶子结点对应的哈夫曼编码*/{start=n-1; /*初始化编码起始指针*/for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent) /*从叶子到根结点求编码*/ if( (*ht)[p].LChild == c)cd[--start]='0'; /*左分支标0*/elsecd[--start]='1'; /*右分支标1*/hc[i]=(char *)malloc((n-start)*sizeof(char)); /*为第i个编码分配空间*/strcpy(hc[i],&cd[start]);}free(cd);for(i=1;i<=n;i++)printf("%d编码为%s\n",(*ht)[i].weight,hc[i]);}int main()HuffmanTree HT;HuffmanCode HC;int *w;int i,n; // the number of elements;int wei; // the weight of a element;int m;printf("input the total number of the Huffman Tree:" ); scanf("%d",&n);w=(int *)malloc((n+1)*sizeof(int));for(i=1;i<=n;i++){printf("input the %d element's weight:",i);fflush(stdin);scanf("%d",&wei);w[i]=wei;}CrtHuffmanTree(&HT,w,n);m = 2*n-1;outputHuffman(HT,m);printf("\n");CrtHuffmanCode(&HT,&HC,n);getchar();getchar();}五、调试分析5.1程序编译界面5.2程序运行界面程序敲完了,发现运行后速度很快,还没看清结果就结束了。