1、数据结构实验报告题目: 专业:班级:组别:组长:完成日期: 年 月 日评分依据及结果态度(A-D)规范性(A-D)完成度(A-D)总评(A-D)评 语代码分工情况姓名工作内容实验报告分工情况姓名耿丽丽工作内容需求分析概要分析一、 需求分析利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。本次设计要求对输入的一串电文字符实现哈夫曼编码,再对哈夫曼编码生成的代码串进行译码,输出电文字符串。系统应具有如下的几个功能:1、初始化(Init):能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立哈夫曼树2、建立编码表(CreateTable):利用已经建好的哈夫
2、曼树进行编码,并将每个字符的编码输出。3、编码(Encoding):根据编码表对输入的字符串进行编码,并将编码后的字符串输出。4、译码(Decoding):利用已经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。5、打印(Print):以直观的方式打印哈夫曼树.二、 概要设计本程序的数据类型定义为typedef struct int weight; int parent,lchild,rchild; HTNode,*HuffmanTree;typedef char* HuffmanCode; /把字母和相应的权值放在一起typedef structchar ch;int wht;WeN
3、u; 所实现的功能函数如下/统计叶子结点的权值void CountWeight(char*str); /选择parent为0,且weight最小的两个节点 序号为s1,s2 void Select(HuffmanTree HT,int len,int &s1,int &s2); / 构造哈夫曼树HTvoid CreatHuffmanTree(HuffmanTree &HT, int n)/ 从叶子到根逆向求每个字符的赫夫曼编码,存储在编码表HC中void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n) /输出编码void Sho
4、wCode(HuffmanTree HT, HuffmanCode HC, int n) /输出哈夫曼void ShowHuffman(HuffmanTree HT, HuffmanCode HC, char *str) /输出字符串void ShowStr(char *str) /选择解码方式void DeCoding();/主函数void main();主要结构如图所示:CreatHuffmanTree ()构造哈夫曼树 CreatHuffmanCode ()构造哈夫曼编码输出字符串Str和解码调用DeCoding()三、详细设计1.统计字符频度自然语言描述:(1)取出字符串中的一个字符(
5、2)遍历所有初始化的哈夫曼树结点(3)如果结点中有记录代表的字符且字符等于取出的字符,说明该字符的叶子存在,则将该结点的权值加1(4)如果所有结点记录的字符均没有与取出的字符一致,说明该字符的叶子不存在,则将结点的字符记为取出字符,并将权重设为1(5)重复以上步骤,直至字符串中所有字符全部遍历伪代码描述:1.for(inti=0;ilength;i+)1.1for(intj=0;jlength;j+)1.1.1if(WordStri=HuffTreej.word)/若字符已被统计,则增加权值即可1.1.1.1权重+;1.1.1.2break;1.1.2elseif(!HuffTreej.wor
6、d)/否则需要一个新结点储存这个字符1.1.2.1HuffTreej.word=WordStri;1.1.2.3 HuffTreej.weight=1;1.1.2.4break;时间复杂度O(N2),空间复杂度S(0)2. 构造哈夫曼树:自然语言描述:(1) 选出权值最小的两个结点,其权值和作为其根结点的权值,最小的结点作为左子树;(2) 次小的做为右子树,不断将两棵子树合升为一棵树。重复及上述过程,直至所有结点全被遍历完。3. 为每个叶子结点编码自然语言描述:(1)初始化一个字符数组Code暂存每个叶子节点的编码;(2)前序遍历哈夫曼树;(3)若结点左右子树都为-1,则将Code复制到编码的
7、Code串,准备返回上一层,编码相位少一位,Code长度-1,返回;(4)否则按照从左到右的顺序前序遍历根结点的所有子树;(5)若访问左子树,则进入下一层,编码相位多一位,Code长度加1并将最后一位赋值为0,访问右子树,进入下一层,Code长度加1并将最后一位赋值为0。4、编码自然语言描述:(1) 定义字符串CodeStr储存编码(2) 遍历输入字符串的每一个字符(3) 对每一个字符,将其与HuffTree前n个叶子节点的word逐个比较,相同则将该节点的编码串Code连接到CodeStr。5、译码自然语言描述:(1)从编码串CodeStr的第一个字符串开始于HuffTree的第一个节点的编
8、码域第一个字符比较(2)相等则比较后面的字符(3)否则,从CodeStr第一个字符与HuffTree第二个结点的编码比较(4) 重复上述过程,将CodeStr中的所有字符比较完毕四、调试分析1.在写程序与调试的过程中,发现自己对于函数的调用与参数的传递的方面还是存在很多问题,从参数的类型等等方面都出现了很多问题。 2.关于这个程序的要求比较复杂,刚开始做的时候没有任何思路,最后分模块一点点的进行。3递归函数中的参数传递在给每个字符编码时,由于编码是从根结点开始,所以选用与前序遍历相似的递归遍历形式。其中需要一个字符型数组来记录路径和编码,由于递归一次才有一位编码,所以这个数组也要随着递归函数的
9、进行而不断修改。开始时我们没有用指针最为参数而是直接将数组作为参数.结果发现每次调用递归函数时数组都是空。原来我用的是值传递,数组就算修改了也无法返回。这提醒了我们之后运用递归函数时如果需要某个变量随函数递归而修改时应该使用地址传递而并非值传递。心得体会:哈夫曼树又称做最优叉树,它是n个带权的叶子结点构成的所有二叉树中,带权路径长度WPL最小的二叉树。在n个带权的叶子结点所构成的二叉树中,满二叉树或完全二叉树不一定 是最优二叉树。权值越大的结点离树根越近的二叉树才是最优叉树。哈夫曼树是根据字符出现的概率来构造平均长度最短的编码。它是-种变长的编码。在编码中,若各码字长度亚格按照码字所对应符号出
10、现概率的大小的逆序排列,则编码的平均长度是最小的。哈夫曼树的应用非常广泛,在通信中,采用0.1的不同排列来表示不同的字符,而哈夫曼树在数据编码中的应用,若每个字符出现的频率相同,则可以采用等长的二进制编码,若频率不同,则可以采用不等长的二进编码,频率较大的采用位数较少的编码,频率较小的字符采用位数较多的编码,这样可以使字符的整体编码长度最小,哈夫曼编码就是一种不等长的二进制编码,且哈夫曼树是一种最优 二叉树,它的编码也是-种最优编码,在哈夫曼树中,规定往左编码为0,往右编码为1,则得到叶子结点编码为从根结点到叶子结点中所有路径中0和1的顺序排列。下一步的改进程序中多次使用了遍历数组或对数据进行
11、逐个比对,循环的次数可以通过计算再减少,提高时间效率。五、用户守则请使用Windows系统来打开,平台为Visual Studio。要注意文本的打开方式和设置六、 测试结果(及截图)七、附录(源代码)头文件#define _CRT_SECURE_NO_WARNINGS#include#include#include#includeusing namespace std;#define NUM 26typedef structchar ch;int wht;WeNu;/把字母和相应的权值放在一起typedef structint weight;int parent, lchild, rchild
12、;HTNode, *HuffmanTree;typedef char *HuffmanCode;源文件/#includeweight.cpp#includeHuffman.hWeNu weightNUM + 1;void CountWeight(char*str)for (int i = 1; i = NUM; i+)char cha = (char)(i + 96);weighti.ch = cha;/简短代码出过错/*switch (i)case 1:weight1.ch = a;break;case 2:weight2.ch = b;break;case 3:weight3.ch = c
13、;break;case 4:weight4.ch = d;break;case 5:weight5.ch = e;break;case 6:weight6.ch = f;break;case 7:weight7.ch = g;break;case 8:weight8.ch = h;break;case 9:weight9.ch = i;break;case 10:weight10.ch = j;break;case 11:weight11.ch = k;break;case 12:weight12.ch = l;break;case 13:weight13.ch = m;break;case
14、14:weight14.ch = n;break;case 15:weight15.ch = o;break;case 16:weight16.ch = p;break;case 17:weight17.ch = q;break;case 18:weight18.ch = r;break;case 19:weight19.ch = s;break;case 20:weight20.ch = t;break;case 21:weight21.ch = u;break;case 22:weight22.ch = v;break;case 23:weight23.ch = w;break;case
15、24:weight24.ch = x;break;case 25:weight25.ch = y;break;case 26:weight26.ch = z;break;default:cout error! endl;*/weighti.wht = 0;int n = strlen(str);for (int i = 0; i n; i+)char c = stri;weight(int)c) - 96.wht+;/减短代码/switch (c) /case a:/weight1.wht+;/break;/case b:/weight2.wht+;/break;/case c:/weight
16、3.wht+;/break;/case d:/weight4.wht+;/break;/case e:/weight5.wht+;/break;/case f:/weight6.wht+;/break;/case g:/weight7.wht+;/break;/case h:/weight8.wht+;/break;/case i:/weight9.wht+;/break;/case j:/weight10.wht+;/break;/case k:/weight11.wht+;/break;/case l:/weight12.wht+;/break;/case m:/weight13.wht+
17、;/break;/case n:/weight14.wht+;/break;/case o:/weight15.wht+;/break;/case p:/weight16.wht+;/break;/case q:/weight17.wht+;/break;/case r:/weight18.wht+;/break;/case s:/weight19.wht+;/break;/case t:/weight20.wht+;/break;/case u:/weight21.wht+;/break;/case v:/weight22.wht+;/break;/case w:/weight23.wht+
18、;/break;/case x:/weight24.wht+;/break;/case y:/weight25.wht+;/break;/case z:/weight26.wht+;/break;/default:/cout error! endl;/*/void Select(HuffmanTree HT, int len, int &s1, int &s2)int i, min1 = 0x3f3f3f3f, min2 = 0x3f3f3f3f;/先赋予最大值for (i = 1; i = len; i+)if (HTi.weight min1&HTi.parent = 0)min1 = H
19、Ti.weight;s1 = i;int temp = HTs1.weight;/将原值存放起来,然后先赋予最大值,防止s1被重复选择HTs1.weight = 0x3f3f3f3f;for (i = 1; i = len; i+)if (HTi.weight min2&HTi.parent = 0)min2 = HTi.weight;s2 = i;HTs1.weight = temp;/恢复原来的值void CreatHuffmanTree(HuffmanTree &HT, int n)/构造赫夫曼树HTint m, s1, s2, i;if (n = 1) return;m = 2 * n
20、 - 1;HT = new HTNodem + 1; /0号单元未用,所以需要动态分配m+1个单元,HTm表示根结点 for (i = 1; i = m; +i) /将1m号单元中的双亲、左孩子,右孩子的下标都初始化为0 HTi.parent = 0; HTi.lchild = -(96 + i); HTi.rchild = -(96 + i);/a的ASCII是97!/cout 请输入叶子结点的权值:n;for (i = 1; i = n; +i) /输入前n个单元中叶子结点的权值 HTi.weight = weighti.wht;/*初始化工作结束,下面开始创建赫夫曼树*/for (i =
21、 n + 1; i = m; +i) /通过n-1次的选择、删除、合并来创建赫夫曼树Select(HT, i - 1, s1, s2);/在HTk(1ki-1)中选择两个其双亲域为0且权值最小的结点,/ 并返回它们在HT中的序号s1和s2HTs1.parent = i;HTs2.parent = i;/得到新结点i,从森林中删除s1,s2,将s1和s2的双亲域由0改为iHTi.lchild = s1;HTi.rchild = s2;/s1,s2分别作为i的左右孩子HTi.weight = HTs1.weight + HTs2.weight; /i 的权值为左右孩子权值之和/for/ Creat
22、HuffmanTreevoid CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)/从叶子到根逆向求每个字符的赫夫曼编码,存储在编码表HC中int i, start, c, f;HC = new char*n + 1; /分配n个字符编码的头指针矢量char *cd = new charn;/分配临时存放编码的动态数组空间cdn - 1 = 0; /编码结束符for (i = 1; i = n; +i) /逐个字符求赫夫曼编码start = n - 1; /start开始时指向最后,即编码结束符位置c = i;f = HTi.par
23、ent; /f指向结点c的双亲结点while (f != 0) /从叶子结点开始向上回溯,直到根结点-start; /回溯一次start向前指一个位置if (HTf.lchild = c)cdstart = 0;/结点c是f的左孩子,则生成代码0elsecdstart = 1; /结点c是f的右孩子,则生成代码1c = f;f = HTf.parent; /继续向上回溯 /求出第i个字符的编码 HCi = new charn - start; / 为第i 个字符编码分配空间strcpy(HCi, &cdstart); /将求得的编码从临时空间cd复制到HC的当前行中delete cd; /释放
24、临时空间void ShowCode(HuffmanTree HT, HuffmanCode HC, int n)for (int i = 1; i = n; i+)cout weighti.ch ( HTi.weight ) 编码为 HCi ttt;if (i % 2 = 0)printf(n);void ShowHuffman(HuffmanTree HT, HuffmanCode HC, char *str)int i = 0;while (stri != 0)char c = stri;cout HC(int)c) - 96;/简短代码/*switch (c) case a:cout H
25、C1;break;case b:cout HC2;break;case c:cout HC3;break;case d:cout HC4;break;case e:cout HC5;break;case f:cout HC6;break;case g:cout HC7;break;case h:cout HC8;break;case i:cout HC9;break;case j:cout HC10;break;case k:cout HC11;break;case l:cout HC12;break;case m:cout HC13;break;case n:cout HC14;break;
26、case o:cout HC15;break;case p:cout HC16;break;case q:cout HC17;break;case r:cout HC18;break;case s:cout HC19;break;case t:cout HC20;break;case u:cout HC21;break;case v:cout HC22;break;case w:cout HC23;break;case x:cout HC24;break;case y:cout HC25;break;case z:cout HC26;break;default:cout error! endl
27、;*/*/i+;void ShowStr(char *str)for (int i = 0; i strlen(str); i+)cout stri;cout endl;void DeCoding(HuffmanTree HT, char *str)/解码int n = strlen(str);int j = 2 * NUM - 1;for (int i = 0; i n; i+)if (stri = 0)j = HTj.lchild;if (HTj.lchild 0)printf(%c, (-HTj.lchild);j = 2 * NUM - 1;else/stri=1j = HTj.rch
28、ild;if (HTj.rchild 0)printf(%c, (-HTj.rchild);j = 2 * NUM - 1;void main()HuffmanTree HT;HuffmanCode HC;char *str1 = (char*)qwetyughjkiopasfdadzxcvbnmjkfghsadtyuiodfgxcv ;char *str2 = (char*)0100010001001101110001011110000111111001100010111011100111000;cout 此字符串为:;ShowStr(str1);CountWeight(str1);CreatHuffmanTree(HT, NUM);CreatHuffmanCode(HT, HC, NUM);ShowCode(HT, HC, NUM);cout 此字符串的Huffman编码为:n;ShowHuffman(HT, HC, str1);cout endl;cout 原始的Huffman为:n;ShowStr(str2);cout 解码过后的字符串为:n;DeCoding(HT, str2);cout endl;system(pause);