实验三 哈夫曼编码
《哈夫曼编码》实验报告

《哈夫曼编码》实验报告《哈夫曼编码》实验报告一、实验目的1、掌握哈夫曼编码原理;2、熟练掌握哈夫曼树的生成方法;3、理解数据编码压缩和译码输出编码的实现。
二、实验要求实现哈夫曼编码和译码的生成算法。
三、实验步骤编写代码如下:#include#include#include#define MAXLEN 100typedef struct{int weight;int lchild;int rchild;int parent;char key;}htnode;typedef htnode hfmt[MAXLEN];int n;void inithfmt(hfmt t){int i;printf("\n");printf("--------------------------------------------------------\n"); printf("**********************输入区**********************\n");printf("\n请输入n=");scanf("%d",&n);getchar();for(i=0;i<2*n-1;i++){t[i].weight=0;t[i].lchild=-1;t[i].rchild=-1;t[i].parent=-1;}printf("\n");}void inputweight(hfmt t){int w;int i;char k;for(i=0;i<n;i++)< bdsfid="112" p=""></n;i++)<>{printf("请输入第%d个字符:",i+1);scanf("%c",&k);getchar();t[i].key=k;printf("请输入第%d个字符的权值:",i+1);scanf("%d",&w);getchar();t[i].weight=w;printf("\n");}}void selectmin(hfmt t,int i,int *p1,int *p2){long min1=999999;long min2=999999;int j;for(j=0;j<=i;j++)if(t[j].parent==-1)if(min1>t[j].weight){min1=t[j].weight;*p1=j;}for(j=0;j<=i;j++)if(t[j].parent==-1)if(min2>t[j].weight && j!=(*p1))//注意 j!=(*p1)) { min2=t[j].weight;*p2=j;}}void creathfmt(hfmt t){int i,p1,p2;inithfmt(t);inputweight(t);for(i=n;i<2*n-1;i++){selectmin(t,i-1,&p1,&p2);t[p1].parent=i;t[p2].parent=i;t[i].lchild=p1;t[i].rchild=p2;t[i].weight=t[p1].weight+t[p2].weight;}}void printhfmt(hfmt t){int i;printf("------------------------------------------------------------------\n");printf("**************哈夫曼编数结构:*********************\n"); printf("\t\t权重\t父母\t左孩子\t右孩子\t字符\t");for(i=0;i<2*n-1;i++){printf("\n");printf("\t\t%d\t%d\t%d\t%d\t%c",t[i].weight,t[i].parent,t[i].lc hild,t [i].rchild,t[i].key);}printf("\n------------------------------------------------------------------\n");printf("\n\n");}void hfmtpath(hfmt t,int i,int j){int a,b;a=i;b=j=t[i].parent;if(t[j].parent!=-1){i=j;hfmtpath(t,i,j);}if(t[b].lchild==a)printf("0");elseprintf("1");}void phfmnode(hfmt t){int i,j,a;printf("\n---------------------------------------------\n"); printf("******************哈夫曼编码**********************"); for(i=0;i<n;i++)< bdsfid="190" p=""></n;i++)<>{j=0;printf("\n");printf("\t\t%c\t",t[i].key,t[i].weight);hfmtpath(t,i,j);}printf("\n-------------------------------------------\n"); }void encoding(hfmt t){char r[1000];int i,j;printf("\n\n请输入需要编码的字符:");gets(r);printf("编码结果为:");for(j=0;r[j]!='\0';j++)for(i=0;i<n;i++)< bdsfid="207" p=""></n;i++)<>if(r[j]==t[i].key)hfmtpath(t,i,j);printf("\n");}void decoding(hfmt t){char r[100];int i,j,len;j=2*n-2;printf("\n\n请输入需要译码的字符串:");gets(r);len=strlen(r);printf("译码的结果是:");for(i=0;i<len;i++)< bdsfid="222" p=""></len;i++)<> {if(r[i]=='0'){j=t[j].lchild;if(t[j].lchild==-1){printf("%c",t[j].key);j=2*n-2;}}else if(r[i]=='1'){j=t[j].rchild;if(t[j].rchild==-1){printf("%c",t[j].key);j=2*n-2;}}printf("\n\n");}int main(){int i,j;hfmt ht;char flag;printf("\n----------------------------------------------\n");printf("*******************编码&&译码&&退出***************");printf("\n【1】编码\t【2】\t译码\t【0】退出");printf("\n您的选择:");flag=getchar();getchar();while(flag!='0'){if(flag=='1')encoding(ht);else if(flag=='2')decoding(ht);elseprintf("您的输入有误,请重新输入。
实验三哈夫曼树编码

华北水利水电学院数据结构实验报告2010~2011学年第二学期2010 级计算机专业班级:176 学号:201017602 姓名:勾志竟一、实验题目:(1)理解哈夫曼树的含义和性质。
(2)掌握哈夫曼树的存储结构以及描述方法。
(3)掌握哈夫曼树的生成方法。
(4)掌握哈夫曼编码的一般方法,并理解其在数据通讯中的应用。
二、实验内容:哈夫曼树与哈弗曼编码、译码问题描述:利用哈弗曼编码进行通信可以大大提高通信利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码。
三、程序源代码:#include <stdio.h>#include <stdlib.h>#define MaxSize 50typedef struct{char c; //代码;int w; //代码权值;char code[MaxSize]; //代码的Huffman编码;}HuffCode[MaxSize];typedef struct{int Weight; //权值;int LChild,RChild,Parent;}HTNode,HuffTree[MaxSize];void HuffmanTree(HuffTree HT,int length,HuffCode hc); //生成Huffman树;void SelectHTNode(HuffTree HT,int n,int *s1,int *s2); //在HT[1..i-1]选择parent为0且weight 最小的两个结点,其序号分别为s1和s2;void HuffmanCode(HuffTree HT,int len,HuffCode hc); //生成Huffman编码;void HuffmanTree(HuffTree HT,int length,HuffCode hc) //Huffman树初始化;{int i,s1,s2;HT[0].Weight = 65535;for(i = 1;i <= length;i++){HT[i].Weight = hc[i].w;HT[i].LChild = HT[i].RChild = HT[i].Parent = -1;}for(;i < 2*length;i++) //i初值 = length+1;{HT[i].LChild = HT[i].RChild = HT[i].Parent = -1;}for(i = length+1;i < 2*length;i++){SelectHTNode(HT,i,&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 SelectHTNode(HuffTree HT,int n,int *s1,int *s2) //在HT[1..i-1]选择parent为0且weight 最小的两个结点,其序号分别为s1和s2;{int i;*s1 = *s2 = 0;for(i = 1;i < n;i++){if(HT[i].Parent == -1){if(HT[*s1].Weight >= HT[i].Weight){*s2 = *s1;*s1 = i;}else if(HT[*s2].Weight > HT[i].Weight) *s2 = i;}}}void HuffmanCode(HuffTree HT,int len,HuffCode hc) //生成Huffman编码;{int i,j,tc,Stack[MaxSize],top = -1;char flag[MaxSize];HTNode th;for(i = 1;i <= len;i++){top = -1; //栈初始化;j = 0; //hc[i].code串首位置偏移;th = HT[i]; //当前结点th;tc = i; //当前结点标记tc;while(th.Parent != -1){ //当前结点th双亲P入栈,由P的孩子是th,确定flag;确定下次结点标记tc; Stack[++top] = th.Parent;if(HT[th.Parent].LChild == tc) {flag[top] = 'L'; tc = th.Parent;}if(HT[th.Parent].RChild == tc) {flag[top] = 'R'; tc = th.Parent;}th = HT[Stack[top]]; //下一结点;}while(top != -1){if(flag[top] == 'L') hc[i].code[j++] ='0';else hc[i].code[j++] ='1';Stack[top--]; //出栈;}hc[i].code[j] ='\0'; //当前串结束;}}int main(void){HuffTree HT; //Huffman树;HuffCode HC; //Huffman编码;int i,len;printf("输入代码数量:"); scanf("%d",&len); system("cls");printf("代码数量:%2d\n\n",len);printf("输入代码和权值(输入格式为:\"c1[回车]\")\n");for(i=1;i <= len;i++){while(getchar() != '\n') NULL;printf("No.%2d: ",i);HC[i].c = getchar();scanf("%d",&HC[i].w); }HuffmanTree(HT,len,HC);HuffmanCode(HT,len,HC);printf("\n输出Huffman编码:\n");for(i = 1;i<=len;i++){ printf("\n %c :",HC[i].c);puts(HC[i].code); }printf("\n\n输出Huffman树结构:");system("pause");printf("\nHT[i]:\t权值\t双亲\t左孩子\t右孩子\n");for(i = 1;i<2*len;i++){ if(i <= len) printf("(%c)",HC[i].c);printf("%2d:\t %2d;\t%2d,\t %2d,\t %2d.\n",i,HT[i].Weight,HT[i].Parent,HT[i].LChild,HT[i].RChild); } return 0;}四、测试结果:代码数量: 8输入代码和权值(输入格式为:"c1[回车]"No. 1: a 5No. 2: b 29No. 3: c 7No. 4: d 8No. 5: e 14No. 6: f 23No. 7: g 3No. 8: h 11输出Huffman编码:a :0001b :11c :1010d :1011e :100f :01g :0000h :001输出Huffman树结构:请按任意键继续. .HT[i]: 权值双亲左孩子右孩(a) 1: 5; 9, -1, -1.(b) 2: 29; 14, -1, -1.(c) 3: 7; 10, -1, -1.(d) 4: 8; 10, -1, -1.(e) 5: 14; 12, -1, -1.(f) 6: 23; 13, -1, -1.(g) 7: 3; 9, -1, -1.(h) 8: 11; 11, -1, -1.9: 8; 11, 7, 1.10: 15; 12, 3, 4.11: 19; 13, 9, 8.12: 29; 14, 5, 10.13: 42; 15, 11, 6.14: 58; 15, 12, 2.15: 100; -1, 13, 14.五、小结(包括收获、心得体会、存在的问题及解决问题的方法、建议等)从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中,利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中,利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
哈夫曼编码的实验报告

哈夫曼编码的实验报告哈夫曼编码的实验报告一、引言信息的传输和存储是现代社会中不可或缺的一部分。
然而,随着信息量的不断增加,如何高效地表示和压缩信息成为了一个重要的问题。
在这个实验报告中,我们将探讨哈夫曼编码这一种高效的信息压缩算法。
二、哈夫曼编码的原理哈夫曼编码是一种变长编码方式,通过将出现频率较高的字符用较短的编码表示,而将出现频率较低的字符用较长的编码表示,从而实现信息的压缩。
它的核心思想是利用统计特性,将出现频率较高的字符用较短的编码表示,从而减少整体编码长度。
三、实验过程1. 统计字符频率在实验中,我们首先需要统计待压缩的文本中各个字符的出现频率。
通过遍历文本,我们可以得到每个字符出现的次数。
2. 构建哈夫曼树根据字符频率,我们可以构建哈夫曼树。
哈夫曼树是一种特殊的二叉树,其中每个叶子节点代表一个字符,并且叶子节点的权值与字符的频率相关。
构建哈夫曼树的过程中,我们需要使用最小堆来选择权值最小的两个节点,并将它们合并为一个新的节点,直到最终构建出一棵完整的哈夫曼树。
3. 生成编码表通过遍历哈夫曼树,我们可以得到每个字符对应的编码。
在遍历过程中,我们记录下每个字符的路径,左边走为0,右边走为1,从而生成编码表。
4. 进行编码和解码在得到编码表后,我们可以将原始文本进行编码,将每个字符替换为对应的编码。
编码后的文本长度将会大大减少。
为了验证编码的正确性,我们还需要进行解码,将编码后的文本还原为原始文本。
四、实验结果我们选取了一段英文文本作为实验数据,并进行了哈夫曼编码。
经过编码后,原始文本长度从1000个字符减少到了500个字符。
解码后的文本与原始文本完全一致,验证了哈夫曼编码的正确性。
五、讨论与总结哈夫曼编码作为一种高效的信息压缩算法,具有广泛的应用前景。
通过将出现频率较高的字符用较短的编码表示,哈夫曼编码可以在一定程度上减小信息的存储和传输成本。
然而,哈夫曼编码也存在一些局限性,例如对于出现频率相近的字符,编码长度可能会相差较大。
信息论课程实验报告—哈夫曼编码

*p2 = j;
}
}
void CreateHuffmanTree(HuffmanTree T)
{
int i,p1,p2;
InitHuffmanTree(T);
InputWeight(T);
for(i = n;i < m;i++)
4)依次继续下去,直至信源最后只剩下两个信源符号为止,将这最后两个信源符号分别用二元码符号“0”和“1”表示;
5)然后从最后—级缩减信源开始,进行回溯,就得到各信源符号所对应的码符号序列,即相应的码字。
四、实验目的:
(1)进一步熟悉Huffman编码过程;(2)掌握C语言递归程序的设计和调试技术。以巩固课堂所学编码理论的知识。
#include "stdio.h"
#include "stdlib.h"
#include <float.h>
#include <math.h>
#define n 8
#define m 2*n-1
typedef struct
{
float weight;
int lchild,rchild,parent;
}
}
void InputWeight(HuffmanTree T)
{
float temp[n] = {0.20,0.18,0.17,0.15,0.15,0.05,0.05,0.05};
for(int i = 0;i < n;i++)
T[i].weight = temp[i];
}
哈夫曼树编码实验报告

哈夫曼树编码实验报告哈夫曼树编码实验报告引言:哈夫曼树编码是一种常用的数据压缩算法,通过对数据进行编码和解码,可以有效地减小数据的存储空间。
本次实验旨在探究哈夫曼树编码的原理和应用,并通过实际案例验证其有效性。
一、哈夫曼树编码原理哈夫曼树编码是一种变长编码方式,根据字符出现的频率来确定不同字符的编码长度。
频率较高的字符编码较短,频率较低的字符编码较长,以达到最佳的数据压缩效果。
1.1 字符频率统计首先,需要对待编码的数据进行字符频率统计。
通过扫描数据,记录每个字符出现的次数,得到字符频率。
1.2 构建哈夫曼树根据字符频率构建哈夫曼树,频率较低的字符作为叶子节点,频率较高的字符作为父节点。
构建哈夫曼树的过程中,需要使用最小堆来维护节点的顺序。
1.3 生成编码表通过遍历哈夫曼树,从根节点到每个叶子节点的路径上的左右分支分别赋予0和1,生成对应的编码表。
1.4 数据编码根据生成的编码表,将待编码的数据进行替换,将每个字符替换为对应的编码。
编码后的数据长度通常会减小,实现了数据的压缩。
1.5 数据解码利用生成的编码表,将编码后的数据进行解码,恢复原始数据。
二、实验过程与结果为了验证哈夫曼树编码的有效性,我们选择了一段文本作为实验数据,并进行了以下步骤:2.1 字符频率统计通过扫描文本,统计每个字符出现的频率。
我们得到了一个字符频率表,其中包含了文本中出现的字符及其对应的频率。
2.2 构建哈夫曼树根据字符频率表,我们使用最小堆构建了哈夫曼树。
频率较低的字符作为叶子节点,频率较高的字符作为父节点。
最终得到了一棵哈夫曼树。
2.3 生成编码表通过遍历哈夫曼树,我们生成了对应的编码表。
编码表中包含了每个字符的编码,用0和1表示。
2.4 数据编码将待编码的文本数据进行替换,将每个字符替换为对应的编码。
编码后的数据长度明显减小,实现了数据的压缩。
2.5 数据解码利用生成的编码表,将编码后的数据进行解码,恢复原始文本数据。
数据结构哈夫曼编码实验报告

数据结构哈夫曼编码实验报告【正文】1.实验目的本实验旨在研究哈夫曼编码的原理和实现方法,通过实验验证哈夫曼编码在数据压缩中的有效性,并分析其应用场景和优缺点。
2.实验原理2.1 哈夫曼编码哈夫曼编码是一种无损数据压缩算法,通过根据字符出现的频率构建一颗哈夫曼树,将频率较高的字符用较短的编码表示,频率较低的字符用较长的编码表示。
哈夫曼编码的编码表是唯一的,且能够实现前缀编码,即一个编码不是另一个编码的前缀。
2.2 构建哈夫曼树构建哈夫曼树的过程如下:1) 将每个字符及其频率作为一个节点,构建一个节点集合。
2) 每次从节点集合中选择出现频率最低的两个节点,构建一个新节点,并将这两个节点从集合中删除。
3) 将新节点加入节点集合。
4) 重复以上步骤,直到节点集合中只有一个节点,这个节点就是哈夫曼树的根节点。
2.3 编码过程根据哈夫曼树,对每个字符进行编码:1) 从根节点开始,根据左子树为0,右子树为1的规则,将编码依次加入编码表。
2) 对于每个字符,根据编码表获取其编码。
3) 将编码存储起来,得到最终的编码序列。
3.实验步骤3.1 数据读取与统计从输入文件中读取字符序列,并统计各个字符的频率。
3.2 构建哈夫曼树根据字符频率构建哈夫曼树。
3.3 构建编码表根据哈夫曼树,构建每个字符的编码表。
3.4 进行编码根据编码表,对输入的字符序列进行编码。
3.5 进行解码根据哈夫曼树,对编码后的序列进行解码。
4.实验结果与分析4.1 压缩率分析计算原始数据和压缩后数据的比值,分析压缩率。
4.2 编码效率分析测试编码过程所需时间,分析编码效率。
4.3 解码效率分析测试解码过程所需时间,分析解码效率。
4.4 应用场景分析分析哈夫曼编码在实际应用中的优势和适用场景。
5.结论通过本次实验,我们深入了解了哈夫曼编码的原理和实现方法,实践了哈夫曼编码的过程,并对其在数据压缩中的有效性进行了验证。
实验结果表明,哈夫曼编码能够实现较高的压缩率和较高的编解码效率。
哈夫曼编码译码器实验报告

哈夫曼编码译码器实验报告实验名称:哈夫曼编码译码器实验一、实验目的:1.了解哈夫曼编码的原理和应用。
2.实现一个哈夫曼编码的编码和译码器。
3.掌握哈夫曼编码的编码和译码过程。
二、实验原理:哈夫曼编码是一种常用的可变长度编码,用于将字符映射到二进制编码。
根据字符出现的频率,建立一个哈夫曼树,出现频率高的字符编码短,出现频率低的字符编码长。
编码过程中,根据已建立的哈夫曼树,将字符替换为对应的二进制编码。
译码过程中,根据已建立的哈夫曼树,将二进制编码替换为对应的字符。
三、实验步骤:1.构建一个哈夫曼树,根据字符出现的频率排序。
频率高的字符在左子树,频率低的字符在右子树。
2.根据建立的哈夫曼树,生成字符对应的编码表,包括字符和对应的二进制编码。
3.输入一个字符串,根据编码表将字符串编码为二进制序列。
4.输入一个二进制序列,根据编码表将二进制序列译码为字符串。
5.比较编码前后字符串的内容,确保译码正确性。
四、实验结果:1.构建哈夫曼树:-字符出现频率:A(2),B(5),C(1),D(3),E(1) -构建的哈夫曼树如下:12/\/\69/\/\3345/\/\/\/\ABCDE2.生成编码表:-A:00-B:01-C:100-D:101-E:1103.编码过程:4.译码过程:5.比较编码前后字符串的内容,结果正确。
五、实验总结:通过本次实验,我了解了哈夫曼编码的原理和应用,并且实现了一个简单的哈夫曼编码的编码和译码器。
在实验过程中,我充分运用了数据结构中的树的知识,构建了一个哈夫曼树,并生成了编码表。
通过编码和译码过程,我进一步巩固了对树的遍历和节点查找的理解。
实验结果表明,本次哈夫曼编码的编码和译码过程正确无误。
在实验的过程中,我发现哈夫曼编码对于频率较高的字符具有较短的编码,从而实现了对字符串的高效压缩。
同时,哈夫曼编码还可以应用于数据传输和存储中,提高数据的传输效率和存储空间的利用率。
通过本次实验,我不仅掌握了哈夫曼编码的编码和译码过程,还深入了解了其实现原理和应用场景,加深了对数据结构和算法的理解和应用能力。
哈夫曼编码实验报告心得

哈夫曼编码实验报告心得简介哈夫曼编码是一种用于数据压缩的算法,在信息论和编码理论中扮演着重要的角色。
它基于将出现频率较高的字符用较短的二进制编码表示,而将较少出现的字符用较长的二进制编码表示,从而达到压缩数据的目的。
在这次实验中,我对哈夫曼编码进行了深入的学习和实践,并对其进行了评估和测试。
通过实验,我对哈夫曼编码有了更深入的了解,并收获了一些宝贵的心得体会。
实验过程步骤一:构建哈夫曼树首先,我需要根据给定的数据集构建哈夫曼树。
在构建哈夫曼树的过程中,我采用了优先队列来保存节点,每次选择权重最小的节点进行合并,直到最终合并成一棵完整的哈夫曼树。
步骤二:生成编码表构建好哈夫曼树之后,我需要根据这棵树生成每个字符对应的二进制编码。
这一步需要按照哈夫曼树的路径从根节点到叶子节点进行遍历,每经过一条左子树的路径,就加上一个0,每经过一条右子树的路径,就加上一个1,直到达到叶子节点为止。
步骤三:进行编码压缩生成编码表之后,我根据编码表对原始数据进行了编码压缩。
将每个字符通过其对应的二进制编码进行替换,得到了压缩后的数据。
步骤四:进行解码还原最后,我对压缩后的数据进行解码还原。
通过对编码表的反向查找,将二进制编码转换为原始字符,得到了还原后的数据。
心得体会通过这次实验,我对哈夫曼编码有了更深入的了解。
一开始我遇到了一些困难,例如如何构建哈夫曼树和生成编码表,但通过查阅相关资料和和老师的指导,我逐渐掌握了相关的知识和技巧。
实验中,我发现哈夫曼编码在压缩数据方面有着很好的效果。
根据实验结果,使用哈夫曼编码可以将原始数据压缩到原来的约50%左右,这对于节省存储空间和加快数据传输速度都有着重要的意义。
另外,在实验过程中,我也意识到了哈夫曼编码的一些局限性。
由于是根据字符出现的频率进行编码,在处理一些重复的字符时,哈夫曼编码的压缩效果并不理想。
此外,哈夫曼编码的编解码速度受到哈夫曼树的构建和编码表的生成等步骤的影响,对于大规模数据的处理并不高效。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2013~2014 学年 班级: 第 二 学期 学号:
数据结构 实验报告
计算机科学与技术(专升本) 专业 姓名:
2013 级
实验三
一、 实验题目: 树的应用——哈夫曼编码进行通信可以大大提高信道的利用率,缩短信息传输的时间,降低传输成本。根据哈 夫曼编码的原理,编写一个程序,在用户输入结点权值的基础上求哈夫曼编码。 从键盘输入若干字符及每个字符出现的频率,将字符出现的频率作为结点的权值,建立哈夫曼树,求 出各字符的哈夫曼编码。要求: 1. 输出存放哈夫曼树的数组 HT 的初态和终态; 2. 输出每个字符的哈夫曼编码; 3. 输入由上述若干字符组成的字符串,对电文进行编码并输出; 4. (选作)输入电文的哈夫曼编码,进行译码并输出。
第 6 页
共 6 页
第 3 页 共 6 页
} void reverse(char a[],int &b) { char t,*p=a; for(int i=0,j=b-1;i<b/2;i++,j--) { t=*(p+i); *(p+i)=*(p+j); *(p+j)=t; } } void main() { char u[N],str[N]; int i,n,k,w[N]; HuffmanTree HT; HuffmanCode HC; printf("输入字符的个数:"); scanf("%d",&n); printf("分别输入这%d 个字符:\n",n); for(i=0;i<n;i++) { scanf("%c",&u[i]); getchar(); } printf("分别输入这%d 个字符对应的权值(以每个节点的频率的 10 倍输入):\n",n); for(i=0;i<n;i++) scanf("%d",&w[i]); printf("存放上述所输入\"字符\"的哈夫曼树的数组 HT 的状态如下:\n"); Huffmancoding(HT,HC,w,n); printf("以下是每个字符所对应的哈夫曼编码:\n"); for (i=1; i<=n; ++i) { printf("第%d 个字符所对应的哈夫曼编码是:",i); printf("%s\n",HC[i]); } printf("\n"); printf("(把上次输入的每个字符再依次输入,并存到 str 字符串中)\n"); printf("请输入:"); scanf("%s",str); k=strlen(str);
第 2 页 共 6 页
unsigned int c,f; char *cd; cd=NULL; HC=(HuffmanCode)malloc((n+1)*sizeof(char *)); /*分配 n 个字符编码的头指针向量 ,0 号单元未用*/ cd=(char *)malloc(n*sizeof(char)); /*分配求编码的临时工作空间*/ for (i=1; i<=n; ++i) { /*逐个字符求赫夫曼编码*/ start=0; /*从各个叶子节点到根节点的逆序编码依次存入 cd,start 为编码开始符*/ 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'; cd[start]='\0'; /*编码结束符'\0'存入 cd*/ k=strlen(cd); /*k 为存入 cd 的编码的长度*/ reverse(cd,k); /*把逆序存入的编码再逆置,此时 cd 中存入的即为从根节点到叶子节点的正序编码*/ HC[i]=(char*)malloc((n-start)*sizeof(char)); /*为第 i 个字符编码分配空间*/ strcpy(HC[i],cd); /*从 cd 复制编码(串)到 HC[i]*/ } free(cd); /*释放工作空间*/ } void Select(HuffmanTree &HT,int &i,int &s1,int &s2) { unsigned int m1,m2; int k; m1=m2=32767; /*m1,m2 为最小和次小节点权值*/ for (k=1; k<=i; k++) { if ((HT[k].parent==0)&&(HT[k].weight<m1)) { m2=m1; s2=s1; m1=HT[k].weight; s1=k; } else if ((HT[k].parent==0)&&(HT[k].weight<m2)) { m2=HT[k].weight; s2=k; } }
第 4 页 共 6 页
printf("分别输入这个字符中每个字符所对应的权值(以每个节点的频率的 10 倍输入):\n",n); for(i=0;i<n;i++) scanf("%d",&w[i]); printf("存放上述所输入\"字符串\"的哈夫曼树的数组 HT 的状态如下:\n"); Huffmancoding(HT,HC,w,k); printf("字符串%s 所对应的\"电文编码\"是:",str); for (i=1; i<=n; ++i) printf("%s",HC[i]); printf("\n"); }
三、程序源代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 80 typedef struct { unsigned int weight; unsigned int parent,lchild,rchild; }HTNode,*HuffmanTree; /*动态分配数组存储赫夫曼树 */ typedef char * *HuffmanCode; /* 动态分配数组存储赫夫曼编码表 */ void Select(HuffmanTree &HT,int &i,int &s1,int &s2); /* 从 i 个节点中选出 parent 为 0 且 weight 最小的两个结点,其序号分别为 s1 和 s2 */ void reverse(char a[],int &b); /*把原字符串逆置*/ void Huffmancoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int &n) { /* w 存放 n 个字符的权值(均>0),构造赫夫曼树 HT,并求出 n 个字符的赫夫曼编码 HC */ int m,i; if (n<=1) return; m=2*n-1; /* m 赫夫曼树的结点总数 */ HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); /* 0 号单元未用 */ HuffmanTree p;
四、测试结果:
一次运行结果分两次截图
第 5 页
共 6 页
五、小结(包括收获、心得体会、存在的问题及解决问题的方法、建议等)
通过本次实验,对哈夫曼树的创建,编码有了更深的掌握,本实验是参考课本 147 页的伪代码算法以 及课件所做,刚开始做的时候,对此算法还不太懂,就照着书写了上去,结果发现很多错误,比如: for(p=HT,i=1;i<=n;++i,++p,++w) *p={*w,0,0,0};这条语句, (1)因为为 HT 分配存储空间后 0 号单元未用, 所以应把 p 指向 HT+1,否则的话,就把第 1 个节点存在了 0 号单元,而第 2 个节点就存在了 1 号单元等一 次类推, (2) 因为指针 p 指向的是哈夫曼树中的节点,所以对其所指向节点的权值、双亲节点、左孩子、 右孩子赋值时,应该用 for (i=n+1;i<=m;++i,++p) {p->weight=0; p->parent=0; p->lchild=0; p->rchild=0;}, 这 两个问题检查了多次才发现,本实验遇到的最大的问题也就是这两个,经过多次反复检查才修改出来。 还有一个就是对各个叶子节点编码时,本实验采用了从叶子节点到根节点逆序编码并存入 cd 所指向 的存储单元中,即第 1 个节点的编码存入 cd[0],第 2 个节点的编码存入 cd[1]等依次类推,最后再调用 逆置字符串函数修改一下 cd 中编码的顺序,调用逆置函数修改之后即为从根节点到叶子节点的正序编码, 本人认为这样做不仅容易理解,而且这样做的结果对哈夫曼树的编码是“不等长编码” ,节省了内存空间。 其它问题都是些小的问题,经过多次反复检查修改即可。 本实验除课本和课件外无任何参考,本人独立完成,建议无。
第 1 页 共 6 页
p=NULL; for (p=HT+1,i=1;i<=n;++i,++p,++w) { /* 构造 n 棵只有根结点的树,因 0 号单元未用,因此 p 指向 HT+1 */ p->weight=*w; p->parent=0; p->lchild=0; p->rchild=0; } for (i=n+1;i<=m;++i,++p) { /*新生成的节点各值都赋值为 0*/ p->weight=0; p->parent=0; p->lchild=0; p->rchild=0; } printf("初态:\n"); printf("node\tweight\tparent\tlchild\trchild\n"); p=HT+1; for(i=1;i<=m;i++,++p) { printf("%d\t%d\t%d\t%d\t%d\n",i,p->weight,p->parent,p->lchild,p->rchild); } printf("\n"); int s1,s2,t; /*s1 和 s2 分别为最小和次小节点*/ for (i=n+1;i<=m;++i ) { /*建赫夫曼树*/ t=i-1;/*便于实参传递,因此把 i-1 赋值给 t*/ s1=s2=0; Select(HT,t,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; } printf("终态:\n"); printf("node\tweight\tparent\tlchild\trchild\n"); p=HT+1; for(i=1;i<=m;i++,++p) { printf("%d\t%d\t%d\t%d\t%d\n",i,p->weight,p->parent,p->lchild,p->rchild); } printf("\n"); /*从叶子到根逆向求每个字符的赫夫曼编码*/ int start,k;