哈夫曼树和哈夫曼编码——实验报告
数据结构大作业-哈夫曼编码实验报告

一.构造过程①统计可知,字符串全长共59个字符,其中字符‘A’-‘F’的出现频数及对应概率(保留两位小数)如图所示:②将每个字符出现概率作为权重,则对于上述给定的6个权值{25,15,14,20,17,9},依次构造6棵只有根节点的二叉树,共同构成森林F;③在森林F中,选取权重最小和次小的两颗树,分别作为新二叉树的左子树、右子树,且该树根节点权值赋左右子树权值之和;④从森林F中删除已选中的两棵子树,把新得到的二叉树加入F 中;⑤重复③④,直到森林F中仅留下一棵树时,得到最终的哈夫曼树HF如图所示:⑥对上述哈夫曼树,将其树中的每个左分支赋0、右分支赋1,则从根结点开始到叶子结点,各分支路径分别得到对应的二进制串,即为给定的每个字符的哈夫曼编码。
二、代码实现源代码如下:#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <string.h>#define N 6 //指定的编码字符数#define M (2 * N - 1) //HT结点数#define MAXWEIGHT 100typedef struct { //哈夫曼树的存储表示int weight;int parent, lchild, rchild;}HTNode;typedef HTNode HuffmanTree[M + 1];typedef char* HuffmanCode[N + 1]; //哈夫曼编码表void Select(HuffmanTree HT, int n, int& s1, int& s2);void CreateHuffmanTree(HuffmanTree& HT, int* w, int n); void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC); void PrintHC(HuffmanCode HC, char ch[]);int main() {HuffmanTree HT;HuffmanCode HC;int w[N + 1];char ch[N + 1];printf("Please input %d characters & its weight(e.g. A 25):\n", N);for (int i = 1; i <= N; i++) {scanf("%c %d", &ch[i], &w[i]);getchar();}CreateHuffmanTree(HT, w, N);CreateHuffmanCode(HT, HC);PrintHC(HC, ch);return 0;}void Select(HuffmanTree HT, int n, int& s1, int& s2) { int i, min = 0, temp = MAXWEIGHT;for (i = 1; i <= n; i++) {if (!HT[i].parent) {if (temp > HT[i].weight) {temp = HT[i].weight;min = i; //最小值作为新树左子树}}}s1 = min;for (i = 1, min = 0, temp = MAXWEIGHT; i <= n; i++) { if ((!HT[i].parent) && i != s1) {if (temp > HT[i].weight) {temp = HT[i].weight;min = i; //次小值作为新树右子树}}}s2 = min;}void CreateHuffmanTree(HuffmanTree& HT, int* w, int n) { //创建哈夫曼树HTint i, s1, s2;if (n <= 1) return; //树为空或仅有根节点for (i = 1; i <= M; ++i) { //初始化HT[i].weight = w[i];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[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 CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC) {int i, start, c, f;char cd[N];cd[N - 1] = '\0'; //编码结束符for (i = 1; i <= N; i++) { //对于第i个待编码字符即第i个带权值的叶子节点start = N - 1; //开始start指向结束符位置c = i;f = HT[i].parent; //f指向当前结点的双亲while (f) { //从叶上溯,到根节点跳出if (HT[f].lchild == c)cd[--start] = '0'; //左孩子elsecd[--start] = '1'; //右孩子c = f;f = HT[f].parent;}HC[i] = new char[N - start];strcpy(HC[i], &cd[start]); //复制结果到编码表,用于输出}}void PrintHC(HuffmanCode HC, char ch[]) { //打印哈夫曼编码表for (int i = 1; i <= N; i++)printf("\n%c: %s\n", ch[i], HC[i]);}三,输出各个字母的哈夫曼编码:四、算法分析1、哈夫曼编码是最优前缀编码:对包括N个字符的数据文件,分别以它们的出现概率构造哈夫曼树,利用该树对应的哈夫曼编码对报文进行编码,得到压缩后的最短二进制编码;2、算法自底而上地构造出对应最优编码的二叉树HT,它从n个叶子结点开始,识别出最低权重的两个对象,并将其合并;当合并时,新对象的权重设置为原两个对象权值之和,一共执行了|n| - 1次合并操作;3、是贪心算法:每次选择均为当下的最优选择,通过合并来构造最优树得到对应的哈夫曼编码;4、复杂度:①时间复杂度:有n个终端节点,构成的哈夫曼树总共有2n-1个节点,Select函数的时间复杂度为O(n),嵌套for循环,因此建立哈夫曼树的时间复杂度为O(n^2),创建哈弗曼编码表的时间复杂度为O(n^2);②空间复杂度:算法中临时占用存储空间大小为数组第0号空间,其耗费的空间复杂度为O(1)。
哈夫曼编码实验报告

一、实验目的1.掌握MATLAB软件的使用,以及其设计流程;2.掌握哈夫曼编码的实现方法;3.用MATLAB语言设计哈夫曼编码的实现方法。
4.提高独立进行算法编程的能力。
二、实验仪器或设备装MATLAB软件的微机一台三、总体设计1、二进制Huffman编码的基本原理及算法1).将信源消息符号按其出现的概率大小依次排列为12...np p p≥≥≥2).取两个概率最小的字母分别配以0和1两个码元,并将这两个概率相加作为一个新字母的概率,与未分配的二进制符号的字母重新排队。
3).对重排后的两个概率最小符号重复步骤2的过程。
4).不断继续上述过程,直到最后两个符号配以0和1为止。
5).从最后一级开始,向前返回得到各个信源符号所对应的码元序列,即相应的码子。
2、程序设计的原理1)程序的输入:以一维数组的形式输入要进行huffman编码的信源符号的概率,在运行该程序前,显示文字提示信息,提示所要输入的概率矢量;然后对输入的概率矢量进行合法性判断,原则为:如果概率矢量中存在小于0的项,则输入不合法,提示重新输入;如果概率矢量的求和大于1,则输入也不合法,提示重新输入。
2)huffman编码具体实现原理:1>在输入的概率矩阵p正确的前提条件下,对p进行排序,并用矩阵L记录p排序之前各元素的顺序,然后将排序后的概率数组p的前两项,即概率最小的两个数加和,得到新的一组概率序列,重复以上过程,最后得到一个记录概率加和过程的矩阵p以及每次排序之前概率顺序的矩阵a。
2>新生成一个n-1行n列,并且每个元素含有n个字符的空白矩阵,然后进行huffman编码:将c矩阵的第n-1行的第一和第二个元素分别令为0和1(表示在编码时,根节点之下的概率较小的元素后补0,概率较大的元素后补1,后面的编码都遵守这个原则)然后对n-i-1的第一、二个元素进行编码,首先在矩阵a中第n-i行找到值为1所在的位置,然后在c矩阵中第n-i行中找到对应位置的编码(该编码即为第n-i-1行第一、二个元素的根节点),则矩阵c的第n-i行的第一、二个元素的n-1的字符为以上求得的编码值,根据之前的规则,第一个元素最后补0,第二个元素最后补1,则完成该行的第一二个元素的编码,最后将该行的其他元素按照“矩阵c中第n-i行第j+1列的值等于对应于a矩阵中第n-i+1行中值为j+1的前面一个元素的位置在c矩阵中的编码值”的原则进行赋值,重复以上过程即可完成huffman编码。
哈夫曼编码实验报告

哈夫曼编码实验报告
几个相关的基本概念:
1.路径:从树中一个结点到另一个结点之间的分支序列构成两个节点间的路径。
2.路径长度:路径上的分支的条数称为路径长度。
3.树的路径长度:从树根到每个结点的路径长度之和称为树的路径长度。
4.结点的权:给树中结点赋予一个数值,该数值称为结点的权。
5.带权路径长度:结点到树根间的路径长度与结点的权的乘积,称为该结点的带权路径长度。
6.树的带权路径长度:树中所有叶子结点的带权路径长度之和,通常记为WPL 。
7.最优二叉树:在叶子个数n以及各叶子的权值确定的条件下,树的带权路径长度WPL值最低的二叉树称为最优二叉树。
哈夫曼树的建立
由哈夫曼最早给出的建立最优二叉树的带有一般规律的算法,俗
称哈夫曼算法。
描述如下:
1)初始化:根据给定的n个权值(W1,W2,…,Wn),构造n棵二叉树的森林集合F={T1,T2,…,Tn},其中每棵二叉树Ti只有一个权值为Wi的根节点,左右子树均为空。
2)找最小树并构造新树:在森林集合F中选取两棵根的权值最小的树做为左右子树构造一棵新的二叉树,新的二叉树的根结点为新增加的结点,其权值为左右子树的权值之和。
3)删除与插入:在森林集合F中删除已选取的两棵根的权值最小的树,同时将新构造的二叉树加入到森林集合F中。
4)重复2)和3)步骤,直至森林集合F中只含一棵树为止,这颗树便是哈夫曼树,即最优二叉树。
由于2)和3)步骤每重复一次,删除掉
两棵树,增加一棵树,所以2)和3)步骤重复n-1次即可获得哈夫曼树。
哈夫曼编码解码实验报告

哈夫曼编码解码实验1.实验要求掌握二叉树的相关概念掌握构造哈夫曼树,进行哈夫曼编码。
对编码内容通过哈夫曼树进行解码。
2.实验内容通过二叉树构造哈夫曼树,并用哈夫曼树对读取的txt文件进行哈夫曼编码。
编码完成后通过哈夫曼树进行解码。
#include<>#include<>#define MAX 100arent=ht[i].lch=ht[i].rch=0;}j=0;for(i=1;i<=n;i++){/*getchar();printf("输入第%d个叶子节点的值:",i);scanf("%c",&ht[i].data);printf("输入该节点的权值:");scanf("%d",&ht[i].weight);*/for(;j<127;j++){if(Coun[j]!=0){ht[i].data=j;ata);ht[i].weight=Coun[j];eight);break;}}j++;}printf("\n");for(i=1;i<=n;i++){printf("%c",ht[i].data);}printf("\n");for(i=n+1;i<=2*n-1;i++){arent==0)eight<min1){min2=min1;right=left;min1=ht[k].weight;left=k;else if (ht[k].weight<min2){min2=ht[k].weight;right=k;}}ht[left].parent=i;ht[right].parent=i;ht[i].weight=ht[left].weight+ht[right].weight;ht[i].lch=left;ht[i].rch =right;}}arent;while(f!=0){if(ht[f].lch==c)[]='0';else[]='1';;f=ht[f].parent;}hcd[i]=cd;}printf("输出哈夫曼编码:\n");for(i=1;i<=n;i++){printf("%c:",ht[i].data);for(k=hcd[i].start+1;k<=n;k++)printf("%c",hcd[i].bit[k]);printf("\n");}}ata){for(k=hcd[j].start+1;k<=n;k++){s1[h]=hcd[j].bit[k];h++;}break;}}i++;}ch;if(ht[f].lch==0&&ht[f].rch==0){C=ht[f].data;printf("%c",C);f=2*n-1;}}else if(s1[i]=='1'){f=ht[f].rch;if(ht[f].lch==0&&ht[f].rch==0){C=ht[f].data;printf("%c",C);f=2*n-1;}}}printf("\n"); }验总结通过本次实验,对二叉树的应用有了相应的了解,掌握了如何构造哈夫曼编码,如何对编码结果进行解码。
数据结构实验三哈夫曼树实验报告

题目:哈夫曼编/译码器一、题目要求:写一个哈夫曼码的编/译码系统,要求能对要传输的报文进行编码和解码。
构造哈夫曼树时,权值小的放左子树,权值大的放右子树,编码时右子树编码为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;}四、调试分析与心得体会:虽然哈夫曼树的建立有书上的参考,但是实际写整个代码的时候还是问题重重。
哈夫曼编码实验报告

哈夫曼编码实验报告
哈夫曼树的构造过程:
从一组权中取最小的两个权数作为叶子构成一个简单的树单元(根为两个权值的合)。
从这组权中去除这两个已经构成树单元的权,并把该树单元的根的权加入其中,重新选两个最小权数构成树单元,重复以上,直至所有权都被填入树中。
什么是最优带权二叉树:
所有叶子元素的权数*深度(可以理解为路径长)的和最小的树,哈夫曼树就是一种最优带权二叉树(待证明)。
为何要找最优二叉树:
哈夫曼编码是最优二叉树的应用之一。
简单来说哈夫曼编码是一种变长编码方式,通过压缩对象中各个元素的出现频度,生成一个特殊的编码表。
编码表中频度(出现次数)越高的元素,表示的编码长度越
短,所有元素的(出现次数*标识编码长度)求和就是压缩后文件的大小,这正是最优二叉树拥有的特性(总权和最小)。
交换操作对除这两个叶子之外的叶子的权重总值不会造成影响,重新计算这两个叶子的权重和即可,假设其余权重和为X。
交换前:T1=X+(n+q)*a+n*c
交换后:T2=X+(n+q)*c+n*a
由于要证T1<T2
X+(n+q)*a+n*c < X+(n+q)*c+n*a
处理后得到q*(a-c)<0
命题中深度差为正数,即要证a<c,即A叶子的权数小于C叶子的权数。
命题中A比C深,且该树是哈夫曼树。
哈夫曼树构造过程是由小到大的,若c<a,那么c会比a先组成单元或者C和A组成单元,先组成单元则C的深度大于A,C和A组成单元则这两个叶子深度相同,都不符合A比C深的命题,得证a<c。
数据结构哈夫曼树实验报告
数据结构哈夫曼树实验报告一、实验内容本次实验的主要内容是哈夫曼树的创建和编码解码。
二、实验目的1. 理解并掌握哈夫曼树的创建过程;2. 理解并掌握哈夫曼编码的原理及其实现方法;3. 掌握哈夫曼树的基本操作,如求哈夫曼编码和哈夫曼解码等;4. 学习如何组织程序结构,运用C++语言实现哈夫曼编码和解码。
三、实验原理哈夫曼树的创建:哈夫曼树的创建过程就是一个不断合并权值最小的两个叶节点的过程。
具体步骤如下:1. 将所有节点加入一个无序的优先队列里;2. 不断地选出两个权值最小的节点,并将它们合并成为一个节点,其权值为这两个节点的权值之和;3. 将新的节点插入到队列中,并继续执行步骤2,直到队列中只剩下一棵树,这就是哈夫曼树。
哈夫曼编码:哈夫曼编码是一种无损压缩编码方式,它根据字符出现的频率来构建编码表,并通过编码表将字符转换成二进制位的字符串。
具体实现方法如下:1. 统计每个字符在文本中出现的频率,用一个数组记录下来;2. 根据字符出现的频率创建哈夫曼树;3. 从根节点开始遍历哈夫曼树,给左分支打上0的标记,给右分支打上1的标记。
遍历每个叶节点,将对应的字符及其对应的编码存储在一个映射表中;4. 遍历文本中的每个字符,查找其对应的编码表,并将编码字符串拼接起来,形成一个完整的编码字符串。
哈夫曼解码就是将编码字符串还原为原始文本的过程。
具体实现方法如下:1. 从根节点开始遍历哈夫曼树,按照编码字符串的位数依次访问左右分支。
如果遇到叶节点,就将对应的字符记录下来,并重新回到根节点继续遍历;2. 重复步骤1,直到编码字符串中的所有位数都被遍历完毕。
四、实验步骤1. 定义编码和解码的结构体以及相关变量;3. 遍历哈夫曼树,得到每个字符的哈夫曼编码,并将编码保存到映射表中;4. 将文本中的每个字符用其对应的哈夫曼编码替换掉,并将编码字符串写入到文件中;5. 使用哈夫曼编码重新构造文本,并将结果输出到文件中。
五、实验总结通过本次实验,我掌握了哈夫曼树的创建和哈夫曼编码的实现方法,也学会了如何用C++语言来组织程序结构,实现哈夫曼编码和解码。
数据结构哈夫曼树编码译码实验报告.doc
数据结构哈夫曼树编码译码实验报告.【详细设计】具体代码实现如下://HaffmanTree.h#include#include#includestruct HuffmanNode //哈夫曼树的一个结点{ int weight; int parent; int lchild,rchild; };class HuffmanTree //哈夫曼树{private: HuffmanNode *Node; //Node[]存放哈夫曼树char *Info; //Info[]存放源文用到的字符——源码,如'a','b','c','d','e',此内容可以放入结点中,不单独设数组存放int LeafNum; //哈夫曼树的叶子个数,也是源码个数public: HuffmanTree(); ~HuffmanTree(); void CreateHuffmanTree(); /*在内存中建立哈夫曼树,存放在Node[]中。
让用户从两种建立哈夫曼树的方法中选择:1.从键盘读入源码字符集个数,每个字符,和每个字符的权重,建立哈夫曼树,并将哈夫曼树写入文件hfmTree中。
2.从文件hfmTree中读入哈夫曼树信息,建立哈夫曼树*/ void CreateHuffmanTreeFromKeyboard(); void CreateHuffmanTreeFromFile(); void Encoder(); /*使用建立好的哈夫曼树(如果不在内存,则从文件hfmTree中读入并建立内存里的哈夫曼树),对文件ToBeTran中的正文进行编码,并将码文写入文件CodeFile中。
ToBeTran的内容可以用记事本等程序编辑产生。
*/ void Decoder(); /*待译码的码文存放在文件CodeFile中,使用建立好的哈夫曼树(如果不在内存,则从文件hfmTree中读入并建立内存里的哈夫曼树)将码文译码,得到的源文写入文件TextFile中,并同时输出到屏幕上。
哈夫曼实验报告
t[k].lc=x1;
t[k].rc=x2;
}
}
void main() {
int i,n=4;
int w[]={7,5,2,4};
JD t[M]; huffman(n,w,t);
printf("后序遍历二叉树结果:\n");
for(i=1;i<=2*n-1;i++)
printf("%d\n",t[i].data);
哈夫曼树的建立:在这个实验中,只是用了一个哈夫曼数建立函数和一个主函数,其目的也只是建立一个简单的哈夫曼树,所以实验数据直接在主函数中给出,也是几个简单的函数,而当哈夫曼树建立好之后,用后序遍历显示出来,是因为后序遍历更加方便。
哈夫曼编码:这个实验比较复杂,既包含了哈夫曼树的建立,哈夫曼编码的生成,代码文件的译码。按向左走记为0、向右走记为1的方法来给各结点编码,输出的哈夫曼编码是个字符在构建好的哈夫曼树中的哈夫曼编码。
定义带有权值、双亲以及左右孩子的结点,动态分配数组存储赫夫曼树,
动态分配数组存储哈夫பைடு நூலகம்编码表。
实验步骤:1)、类型及相关变量定义。
2)、哈夫曼树的建立。
3)、生成哈夫曼编码。
4)、代码文件的译码。
5)、调试程序并运行测试。
6)、分析结果并讨论、总结。
二、实验结果与分析
1、实验目的、实验思路等见实验设计方案
int i,j,k,x1,x2,m1,m2;
for(i=1;i<(2*n);i++) {
t[i].pa=t[i].lc=t[i].rc=0;
if(i<=n)
哈夫曼编码解码实验报告
哈夫曼编码解码实验1.实验要求掌握二叉树的相关概念掌握构造哈夫曼树,进行哈夫曼编码。
对编码容通过哈夫曼树进行解码。
2.实验容通过二叉树构造哈夫曼树,并用哈夫曼树对读取的txt文件进行哈夫曼编码。
编码完成后通过哈夫曼树进行解码。
#include<stdio.h>#include<string.h>#define MAX 100//定义哈夫曼树的存储结构typedef struct{char data;int weight;int parent;int lch;int rch;}HuffNode;//定义哈夫曼编码的存储结构typedef struct{char bit[MAX];int start;}HuffCode;HuffNode ht[2*MAX];HuffCode hcd[MAX];int Coun[127]={0};int n;char s1[200000];char text[5000];//构造哈夫曼树void HuffmanTree(){int i,j,k,left,right,min1,min2;//printf("输入叶子的节点数:");//scanf("%d",&n);printf("字符数量=%d\n",n);for(i=1;i<=2*n-1;i++){ht[i].parent=ht[i].lch=ht[i].rch=0;}j=0;for(i=1;i<=n;i++){/*getchar();printf("输入第%d个叶子节点的值:",i);scanf("%c",&ht[i].data);printf("输入该节点的权值:");scanf("%d",&ht[i].weight);*/for(;j<127;j++){if(Coun[j]!=0){ht[i].data=j;//printf("%c",ht[i].data);ht[i].weight=Coun[j];//printf("%d",ht[i].weight);break;}}j++;}printf("\n");for(i=1;i<=n;i++){printf("%c",ht[i].data);}printf("\n");for(i=n+1;i<=2*n-1;i++){//在前n个结点中选取权值最小的两个结点构成一颗二叉树min1=min2=10000;//为min1和min2设置一个比所有权值都大的值left=right=0;for(k=1;k<=i-1;k++){if(ht[k].parent==0)//若是根结点//令min1和min2为最小的两个权值,left和right 为权值最小的两个结点位置if(ht[k].weight<min1){min2=min1;right=left;min1=ht[k].weight;left=k;}else if (ht[k].weight<min2){min2=ht[k].weight;right=k;}}ht[left].parent=i;ht[right].parent=i;ht[i].weight=ht[left].weight+ht[right].weight;ht[i].lch=left;ht[i].rch =right;}}//构造哈夫曼编码void HuffmanCode(){int i,c,k,f;HuffCode cd;for(i=1;i<=n;i++){cd.start=n;c=i;f=ht[i].parent;while(f!=0){if(ht[f].lch==c)cd.bit[cd.start]='0';elsecd.bit[cd.start]='1';cd.start--;c=f;f=ht[f].parent;}hcd[i]=cd;}printf("输出哈夫曼编码:\n");for(i=1;i<=n;i++){printf("%c:",ht[i].data);for(k=hcd[i].start+1;k<=n;k++)printf("%c",hcd[i].bit[k]);printf("\n");}}//对字母进行编码void Code()//将字符与相应的哈夫曼编码进行匹配,输出编码结果{int i=0,j,k,h=0;while(text[i]!='\0'){for(j=1;j<=n;j++){if(text[i]==ht[j].data){for(k=hcd[j].start+1;k<=n;k++){s1[h]=hcd[j].bit[k];h++;}break;}}i++;}//printf("编码\n");//puts(s1);//printf("\n");}//解码void HuffmanDecode(){printf("解码\n");int len,i,f;char C;//char S[MAXCODE];//scanf("%s",S);//使用gets()直接跳过len=strlen(s1);printf("s1:%d\n",len);f=2*n-1;for(i=0;i<len;i++){if(s1[i]=='0'){f=ht[f].lch;if(ht[f].lch==0&&ht[f].rch==0){C=ht[f].data;printf("%c",C);f=2*n-1;}}else if(s1[i]=='1'){f=ht[f].rch;if(ht[f].lch==0&&ht[f].rch==0){C=ht[f].data;printf("%c",C);f=2*n-1;}}}printf("\n");}//统计字母个数及其权值void Count(){int i,j,m;n=0;i=0;//printf("请仅输入小写字母\n");//例程本省存在一个BUG,只输入一个字母不能进行编码(并未解决)//scanf("%s",s);while(text[i]!='\0')//使用ASCII码表进行统计{m=text[i];//printf("%d\n",m);Coun[m]++;i++;}for(j=0;j<127;j++){if(Coun[j]!=0)n++;}}//mark Codevoid main(){int l=0;FILE *fp;fp=fopen("text.txt","r");if(fp==NULL){printf("文件打开失败\n");while(1);}while(!feof(fp)){text[l] = fgetc(fp);l++;}printf("输入文本\n");printf("%s\n",text);fclose(fp);Count();HuffmanTree();HuffmanCode();Code();HuffmanDecode();}文本文件文本输入进行哈夫曼编码对文本进行编码输出解码结果3.实验总结通过本次实验,对二叉树的应用有了相应的了解,掌握了如何构造哈夫曼编码,如何对编码结果进行解码。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验报告
学生姓名 学号
专业 年级、班级
课程名称 实验项目 排队系统的模拟 实验类型✓ 综合实验时间 年 月 日
实验指导老师 实验评分
1、实验项目名称
哈夫曼树和哈夫曼编码
2、实验目的及要求
掌握哈夫曼树和哈夫曼编码的实现
3、实验内容及步骤
(1)新建一个工程——Win32 Console Application,为工程取名——创建一个简单的程序【S】
(2)进入类视图,添加类——鼠标右键,点击New Class——输入类的名称,点击确定。
(3)将对应代码放入对应文件——5-13代码放入 hfTree.h文件;5-14、5.15代码放入hfTree.cpp文件;5-16代码放入幂函数文件ex 007.cpp。
(//后绿色的字是在分析代码)
(4)组建、编译,运行结果
4、实验结果及分析
5-13 哈夫曼树类的定义代码分析
template <class Type>//模板类
class hfTree{
private:
struct Node//数组中的元素类型:一个结点
{ Type data;//结点值
int weight;//结点的权值
int parent,left,right;//父结点以及左右儿子的下标地址
};
Node *elem;//定义一个数组,elem是数组的起始位置,哈夫曼树被保存在一个数组中
int length;//数组的规模长度
public:
struct hfCode{//保存赫夫曼编码的类型
Type data;//待编码的字符
string code;//对应的哈夫曼编码
};
hfTree(const Type*x,const int*w,int size);//构造函数,构造函数接收一组待编码的字符以及对应的权值,构造一棵哈夫曼树
void getCode(hfCode result[]);//返回哈夫曼编码的函数
~hfTree(){delete []elem;}//析构函数,释放存储空间
};
5-14 哈夫曼树的构造函数代码分析
template <class Type>
hfTree<Type>::hfTree(const Type*v,const int *w,int size)//哈夫曼树的构造函数,有3个参数,一组待编码的符号,符号对应的权值,前面两个数组的数组规模
{ const int MAX_INT=32767;
int min1,min2;//最小树、次小树的权值
int x,y;//最小树、次小树的下标
length=2*size;//保存哈夫曼树的数组规模是符号数组、权值数组的两倍
elem=new Node[length];//申请一个保存哈夫曼树的数组
for(int i=size;i<length;++i)//for循环为数组赋初值
{ elem[i].weight=w[i-size];//将待编码的符号对应的权值放到数组的后半部分
elem[i].data=v[i-size];//将待编码的符号放到数组的后半部分 elem[i].parent=elem[i].left=elem[i].right=0;//将符号的父结点以及左右儿子都设为0,表明它们都是只有根结点的树
}
for(i=size-1;i>0;--i)//for循环完成size-1次的归并
{ min1=min2=MAX_INT;x=y=0;// min1,min2分别保存两棵权值最小的数的权值,x、y分别表示这两棵树的树根在数组中的下标
for(int j=i+1;j<length;++j)//找出父结点为0,且权值最小和次小的两棵树,等待归并的两棵树
if(elem[j].parent==0)
if(elem[j].weight<min1)
{min2=min1;min1=elem[j].weight;x=y;y=j;}
else if(elem[j].weight<min2)
{min2=elem[j].weight;x=j;}
elem[i].weight=min1+min2;//归并这两棵树,形成新树i,i这棵树的权值=min1+min2(分别保存挑选出的两棵权值最小的树的权值) elem[i].left=x;elem[i].right=y;elem[i].parent=0;//将挑选出来
的两个结点作为左、右子树,构建一棵新树,i为根结点
elem[x].parent=i;elem[y].parent=i;//i是挑选出来的两个结点的父结点
}
}
5-15 哈夫曼的getCode函数代码分析
template <class Type>
void hfTree<Type>::getCode(hfCode result[])//哈夫曼树的getCode函数,返回的是一个数组,数组元素类型是hfCode,每一个元素包含待编码的符号和它对应的编码,编码是一个比特串,用字符串类型表示
{ int size=length/2;//符号数组规模=哈夫曼数组规模/2
int p,s;//s是追溯过程中正在处理结点的下标,p是s的父结点下标
for(int i=size;i<length;++i)//读取每一个符号
{result[i-size].data=elem[i].data; // result数组中第一个元素符号是哈夫曼数组elem中间的元素符号,因为哈夫曼数组elem包含了元素符号数组和元素符号权值数组,而符号数组规模=权值数组规模,所以哈夫曼数组elem数组是它们的两倍。
现在要取出符号,所以从哈夫曼数组elem一半开始取
result[i-size].code="";//当前结点对应的哈夫曼编码
p=elem[i].parent;s=i;
while(p){//向根追溯
if(elem[p].left==s)//若发现当前结点是根结点的左儿子
result[i-size].code='0'+result[i-size].code;//则在字符串前添加0
else result[i-size].code='1'+result[i-size].code; //若发现当前结点是根结点的右儿子,则在字符串前添加1
s=p;p=elem[p].parent;//继续往上一层移动,p为更大树的根结点,s 取代原先p的位置
}
}
}
5-16 哈夫曼树的使用实例代码分析
int main()
{ char ch[]={"aeistdn"};//待编码的字符
int w[]={10,15,12,3,4,13,1};//待编码的字符对应的权值
hfTree<char>tree(ch,w,7);//构造函数,字符数组ch,权值数组w,数组规模7
hfTree<char>::hfCode result[7];//定义一个存放编码的数组hfCode tree.getCode(result);//调用getCode函数
for(int i=0;i<7;++i)//for循环,输出字符以及字符对应的哈夫曼编码 cout<<result[i].data<<' '<<result[i].code<<endl;
return 0;
}。