北邮信通院数据结构实验报告三哈夫曼编码器
- 格式:doc
- 大小:306.00 KB
- 文档页数:20
《哈夫曼编码》实验报告《哈夫曼编码》实验报告一、实验目的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("您的输入有误,请重新输入。
实验报告课程名称:数据结构实验名称:赫夫曼编码及应用院(系):计算机与通信工程学院专业班级:计算机科学与技术姓名:学号:指导教师:2020 年 5 月12 日一、实验目的掌握赫夫曼树和赫夫曼编码的基本思想和算法的程序实现。
二、实验内容及要求1、任务描述a.提取原始文件中的数据(包括中文、英文或其他字符),根据数据出现的频率为权重,b.构建Huffman编码表;c.根据Huffman编码表对原始文件进行加密,得到加密文件并保存到硬盘上;d.将加密文件进行解密,得到解码文件并保存点硬盘上;e.比对原始文件和解码文件的一致性,得出是否一致的结论。
2、主要数据类型与变量a.对Huffman树采用双亲孩子表示法,便于在加密与解密时的操作。
typedef struct Huffman* HuffmanTree;struct Huffman{unsigned int weight; //权值unsigned int p, l, r;//双亲,左右孩子};b.对文本中出现的所有字符用链表进行存储。
typedef struct statistics* List;struct statistics {char str; //存储此字符int Frequency; //出现的频率(次数)string FinalNum; //Huffman编码struct statistics* Next;};3、算法或程序模块对读取到的文本进行逐字符遍历,统计每个字符出现的次数,并记录在创建的链表中。
借助Huffman树结构,生成结构数组,先存储在文本中出现的所有字符以及它们出现的频率(即权值),当作树的叶子节点。
再根据叶子节点生成它们的双亲节点,同样存入Huffman树中。
在完成对Huffman树的创建与存储之后,根据树节点的双亲节点域以及孩子节点域,生成每个字符的Huffman编码,并存入该字符所在链表节点的FinalNum域。
北邮数据结构实验报告北京邮电大学信息与通信工程学院2009级数据结构实验报告实验名称:实验三哈夫曼编/解码器的实现学生姓名:陈聪捷日期:2010年11月28日1.实验要求一、实验目的:了解哈夫曼树的思想和相关概念;二、实验内容:利用二叉树结构实现哈夫曼编/解码器1.初始化:能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立哈夫曼树。
2.建立编码表:利用已经建好的哈夫曼树进行编码,并将每个字符的编码输出。
3.编码:根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4.译码:利用已经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。
5.打印:以直观的方式打印哈夫曼树。
6.计算输入的字符串编码前和编码后的长度,并进行分析,讨论哈夫曼编码的压缩效果。
7.用户界面可以设计成“菜单”方式,能进行交互,根据输入的字符串中每个字符出现的次数统计频度,对没有出现的字符一律不用编码。
2.程序分析2.1存储结构二叉树templateclassBiTree{public:BiTree();//构造函数,其前序序列由键盘输入~BiTree(void);//析构函数BiNode*Getroot();//获得指向根结点的指针protected:BiNode*root;//指向根结点的头指针};//声明类BiTree及定义结构BiNodeData:二叉树是由一个根结点和两棵互不相交的左右子树构成data:HCode*HCodeTable;//编码表inttSize;//编码表中的总字符数二叉树的节点结构templatestructBiNode//二叉树的结点结构{Tdata;//记录数据Tlchild;//左孩子Trchild;//右孩子Tparent;//双亲};编码表的节点结构structHCode{chardata;//编码表中的字符charcode[100];//该字符对应的编码};待编码字符串由键盘输入,输入时用链表存储,链表节点为structNode{charcharacter;//输入的字符unsignedintcount;//该字符的权值boolused;//建立树的时候该字符是否使用过Node*next;//保存下一个节点的地址};示意图:2.2关键算法分析1.初始化函数(voidHuffmanTree::Init(stringInput))算法伪代码:1.初始化链表的头结点2.获得输入字符串的第一个字符,并将其插入到链表尾部,n=1(n 记录的是链表中字符的个数)3.从字符串第2个字符开始,逐个取出字符串中的字符3.1将当前取出的字符与链表中已经存在的字符逐个比较,如果当前取出的字符与链表中已经存在的某个字符相同,则链表中该字符的权值加1。
数据结构哈夫曼编码实验报告数据结构哈夫曼编码实验报告1·实验目的1·1 理解哈夫曼编码的基本原理1·2 掌握哈夫曼编码的算法实现方式1·3 熟悉哈夫曼编码在数据压缩中的应用2·实验背景2·1 哈夫曼编码的概念和作用2·2 哈夫曼编码的原理和算法2·3 哈夫曼编码在数据压缩中的应用3·实验环境3·1 硬件环境:计算机、CPU、内存等3·2 软件环境:编程语言、编译器等4·实验过程4·1 构建哈夫曼树4·1·1 哈夫曼树的构建原理4·1·2 哈夫曼树的构建算法4·2 哈夫曼编码4·2·1 哈夫曼编码的原理4·2·2 哈夫曼编码的算法4·3 实现数据压缩4·3·1 数据压缩的概念和作用4·3·2 哈夫曼编码在数据压缩中的应用方法5·实验结果5·1 构建的哈夫曼树示例图5·2 哈夫曼编码表5·3 数据压缩前后的文件大小对比5·4 数据解压缩的正确性验证6·实验分析6·1 哈夫曼编码的优点和应用场景分析6·2 数据压缩效果的评估和对比分析6·3 实验中遇到的问题和解决方法7·实验总结7·1 实验所获得的成果和收获7·2 实验中存在的不足和改进方向7·3 实验对于数据结构学习的启示和意义附件列表:1·实验所用的源代码文件2·实验中用到的测试数据文件注释:1·哈夫曼编码:一种用于数据压缩的编码方法,根据字符出现频率构建树形结构,实现高频字符用较短编码表示,低频字符用较长编码表示。
2·哈夫曼树:由哈夫曼编码算法构建的一种特殊的二叉树,用于表示字符编码的结构。
数据结构哈夫曼编码实验报告数据结构哈夫曼编码实验报告1. 实验目的本实验旨在通过实践理解哈夫曼编码的原理和实现方法,加深对数据结构中树的理解,并掌握使用Python编写哈夫曼编码的能力。
2. 实验原理哈夫曼编码是一种用于无损数据压缩的算法,通过根据字符出现的频率构建一棵哈夫曼树,并根据哈夫曼树对应的编码。
根据哈夫曼树的特性,频率较低的字符具有较长的编码,而频率较高的字符具有较短的编码,从而实现了对数据的有效压缩。
实现哈夫曼编码的主要步骤如下:1. 统计输入文本中每个字符的频率。
2. 根据字符频率构建哈夫曼树,其中树的叶子节点代表字符,内部节点代表字符频率的累加。
3. 遍历哈夫曼树,根据左右子树的关系对应的哈夫曼编码。
4. 使用的哈夫曼编码对输入文本进行编码。
5. 将编码后的二进制数据保存到文件,同时保存用于解码的哈夫曼树结构。
6. 对编码后的文件进行解码,还原原始文本。
3. 实验过程3.1 统计字符频率首先,我们需要统计输入文本中每个字符出现的频率。
可以使用Python中的字典数据结构来记录字符频率。
遍历输入文本的每个字符,将字符添加到字典中,并递增相应字符频率的计数。
```pythondef count_frequency(text):frequency = {}for char in text:if char in frequency:frequency[char] += 1else:frequency[char] = 1return frequency```3.2 构建哈夫曼树根据字符频率构建哈夫曼树是哈夫曼编码的核心步骤。
我们可以使用最小堆(优先队列)来高效地构建哈夫曼树。
首先,将每个字符频率作为节点存储到最小堆中。
然后,从最小堆中取出频率最小的两个节点,将它们作为子树构建成一个新的节点,新节点的频率等于两个子节点频率的和。
将新节点重新插入最小堆,并重复该过程,直到最小堆中只剩下一个节点,即哈夫曼树的根节点。
数据结构哈夫曼编码实验报告数据结构哈夫曼编码实验报告一、实验背景1:引言在日常生活中,信息传输已经成为了一个非常重要的环节。
通过对信息进行编码,可以有效地减少信息传输的开销和存储空间。
哈夫曼编码是一种常见的无损数据压缩方法,广泛应用于图像、音频和视频等领域。
本实验旨在通过实现哈夫曼编码算法,深入理解其工作原理,并对其性能进行评估。
2:实验目的本实验旨在:a:了解哈夫曼编码算法的基本原理;b:实现哈夫曼编码算法,并将其应用于对文本进行压缩;c:评估哈夫曼编码算法在不同文本数据上的性能。
二、实验内容1:哈夫曼编码原理介绍2:哈夫曼编码的实现思路a:构建哈夫曼树b:哈夫曼编码表c:对文本进行编码和解码3:实验环境介绍a:硬件环境b:软件环境4:实验步骤详解a:构建哈夫曼树的实现方法b:哈夫曼编码表的实现方法c:文本编码和解码的实现方法5:实验数据与结果分析a:不同文本数据的压缩结果对比 b:压缩性能的评估指标6:实验心得与建议a:实验过程中遇到的问题b:改进与优化方向三、实验结果与分析1:实验数据a:不同文本数据的大小与内容b:压缩率等性能指标数据2:实验结果分析a:不同文本数据对压缩效果的影响b:压缩率与文本数据的关系c:哈夫曼编码的运行时间分析四、结论根据实验结果和分析,可以得出以下结论:1:哈夫曼编码算法能够有效地减少文本数据的存储空间。
2:不同文本数据的压缩率存在差异,与文本的特性有关。
3:哈夫曼编码算法的运行时间与文本数据的长度成正比关系。
附件:1:实验源代码2:实验数据和结果法律名词及注释:1:无损数据压缩:指通过编码和解码过程,在不导致数据信息损失的情况下减少数据量。
2:哈夫曼编码:一种变长编码方式,通过更少的编码长度来表示频率较高的字符,从而达到减少编码长度的目的。
11.实验要求利用二叉树结构实现哈夫曼编/ 解码器(1). 初始化:能够对输入的任意长度的字符串s 进行统计,统计每个字符的频度,并建立哈夫曼树。
(2). 建立编码表:利用已经建好的哈夫曼树进行编码,并将每个字符的编码输出。
(3). 编码:根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
(4). 译码:利用已经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。
(5). 打印:以直观的方式打印哈夫曼树。
(6). 计算输入的字符串编码前和编码后的长度,并进行分析,讨论哈夫曼编码的压缩效果。
(7). 用户界面可以设计成“菜单”方式,能进行交互,根据输入的字符串中每个字符出现的次数统计频度,对没有出现的字符一律不用编码。
2.程序分析2.1存储结构二叉树:示意图:root2.21{2.3 关键算法分析1. 定义哈夫曼树的模板类#include <iostream>#include <string.h> using namespace std; structLNode {char ch;int weight;char code[20];LNode* next; };struct TNode{int weight; //int Lchild; //int Rchild; //int Parent; // };class Huffman 结点权值左孩子指针右孩子指针双亲指针// 链表的节点, 用来统计字符频率,并编码// 字符// 权值// 字符编码// 指向下一个节点// 哈弗曼树结点的结构体1 public:Huffman(); ~Huffman(); void CreateTable(); void PrintTable(); void Encoding(); void Decoding(); void Comrate();// 构造函数,输入、统计字符,创建哈弗曼树、码表// 释放链表空间、哈弗曼树的节点// 建立编码表,并将信息存入链表// 输出码表// 哈弗曼编码// 译码void SelectMin(int &x,int &y,int begin,int end);void reverse(char ch[]); voidcontrol();private: // 选取权值最小的两个数,创建哈弗曼树// 将码值倒置,用来编码// 对菜单交互等提示操作TNode* troot;LNode* lroot; void List(char a[]); void HTree(); int Letter; char astr[1000]; char bstr[1000]; // 在统计字符频率是构建链表的根节点// 统计字符的权值建立的单链表// 哈弗曼树建立// 共有不同字符总数// 用户输入的一串字符// 将字符串的码值保存Huffman::Huffman(){lroot=new LNode;bstr[0]='\0';lroot->next=NULL;Letter=0; // 初始化字符总数为1 cout<<" 请输入一串字符,以回车键结束"<<endl;cin.getline(astr,1000,'\n');if(strlen(astr)==0) throw 1;else{List(astr); // 用链表存储每个字符HTree();CreateTable();Encoding();}};Huffman::~Huffman(){delete troot;LNode* p=lroot;while(p=lroot->next)1{{ lroot=p->next; delete p; p=lroot;}delete p; };2. 建立哈夫曼树void Huffman::HTree(){LNode* p=lroot; int a=0;troot=new TNode[2*Letter-1]; //2n-1 while (p=p->next){troot[a].weight=p->weight; troot[a].Parent=-1; troot[a].Lchild=-1; troot[a].Rchild=-1; a++;};for (int i=Letter;i<2*Letter-1;i++)troot[i].Parent=-1; int x,y,begin=0;for (int j=Letter;j<2*Letter-1;j++) while (troot[begin].Parent!=-1)begin++;个结点// 建立叶子节点// 是两个最小值的角标SelectMin(x,y,begin,j);troot[j].weight=troot[x].weight+troot[y].weight;troot[j].Lchild=x;troot[j].Rchild=y;troot[j].Parent=-1;troot[x].Parent=j;troot[y].Parent=j;}};3.统计字符的频率void Huffman::List(char a[]){LNode *p=lroot;int i=0;while(a[i]!='\0'){{while (p&&p->ch!=a[i]) // 查找链表中没有该字符或者找到该字符p=p->next;if (!p) // 如果没有该字符,创建节点。
数据结构哈夫曼编码实验报告【正文】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.结论通过本次实验,我们深入了解了哈夫曼编码的原理和实现方法,实践了哈夫曼编码的过程,并对其在数据压缩中的有效性进行了验证。
实验结果表明,哈夫曼编码能够实现较高的压缩率和较高的编解码效率。
数据结构实验报告实验名称:实验三树——哈夫曼编/解码器学生姓名:班级:班内序号:学号:日期:2014年12月11日1.实验要求利用二叉树结构实现赫夫曼编/解码器。
基本要求:1、初始化(Init):能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立赫夫曼树2、建立编码表(CreateTable):利用已经建好的赫夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4、译码(Decoding):利用已经建好的赫夫曼树对编码后的字符串进行译码,并输出译码结果。
5、打印(Print):以直观的方式打印赫夫曼树(选作)6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫曼编码的压缩效果。
测试数据:I love data Structure, I love Computer。
I will try my best to study data Structure.提示:1、用户界面可以设计为“菜单”方式:能够进行交互。
2、根据输入的字符串中每个字符出现的次数统计频度,对没有出现的字符一律不用编码。
2. 程序分析2.1 存储结构Huffman树给定一组具有确定权值的叶子结点,可以构造出不同的二叉树,其中带权路径长度最小的二叉树称为Huffman树,也叫做最优二叉树。
weight lchild rchild parent2-1-1-15-1-1-16-1-1-17-1-1-19-1-1-1weight lchild rchild parent 2-1-155-1-156-1-167-1-169-1-17 701713238165482967-12.2 关键算法分析(1)计算出现字符的权值利用ASCII码统计出现字符的次数,再将未出现的字符进行筛选,将出现的字符及頻数存储在数组a[]中。
void Huffman::Init(){int nNum[256]= {0}; //记录每一个字符出现的次数int ch = cin.get();int i=0;while((ch!='\r') && (ch!='\n')){nNum[ch]++; //统计字符出现的次数str[i++] = ch; //记录原始字符串ch = cin.get(); //读取下一个字符}str[i]='\0';n = 0;for ( i=0;i<256;i++){if (nNum[i]>0) //若nNum[i]==0,字符未出现{l[n] = (char)i;a[n] = nNum[i];n++;}}}时间复杂度为O(1);(2)创建哈夫曼树:算法过程:Huffman树采用顺序存储---数组;数组的前n个结点存储叶子结点,然后是分支结点,最后是根结点;首先初始化叶子结点元素—循环实现;以循环结构,实现分支结点的合成,合成规则按照huffman树构成规则进行。
关键点:选择最小和次小结点合成。
void Huffman::CreateHTree(){HTree = new HNode [2*n-1]; //根据权重数组a[0..n-1] 初始化Huffman树for (int j = 0; j < n; j++){HTree[j].weight = a[j];HTree[j].LChild = HTree[j].RChild = HTree[j].parent = -1;}int x,y;for (int i = n; i < 2*n-1; i++) //开始建Huffman树{SelectMin( HTree, i, x, y); //从1~i中选出两个权值最小的结点HTree[x].parent = HTree[y].parent = i;HTree[i].weight = HTree[x].weight+ HTree[y].weight;HTree[i].LChild = x;HTree[i].RChild = y;HTree[i].parent = -1;}}时间复杂度为O(n2)void Huffman::SelectMin( HNode *hTree,int n, int &i1, int &i2 ){int i;//找一个比较值的起始值for(i=0; i<n; i++) //找i1{ if(hTree[i].parent==-1 ){ i1=i; break; }}i++;for( ; i<n; i++) //找i2{ if(hTree[i].parent==-1 ){ i2=i; break; }}if(hTree[i1].weight>hTree[i2].weight) //i1指向最小的{ int j=i2; i2=i1; i1 = j; } //开始找最小的两个i++;for( ; i<n; i++){ if(hTree[i].parent==-1&& hTree[i].weight < hTree[i1].weight ){ i2=i1; i1 = i; }else if( hTree[i].parent==-1&& hTree[i].weight < hTree[i2].weight){ i2=i; }}}时间复杂度为O(n)(3)创建编码表算法过程:从叶子到根---自底向上首先定义码表存储空间;循环对n个叶子结点自底向上回溯到根,记下途径的左右关系,形成编码的逆序串;将各个叶子结点对应的逆序串反序即可。
void Huffman::CreateCodeTable(){HCodeTable = new HCode[n]; //生成编码表for (int i=0;i<n;i++){HCodeTable[i].data = l[i];int child = i; //孩子结点编号int parent = HTree[i].parent; //当前结点的父结点编号int k=0;while(parent!=-1){if (child==HTree[parent].LChild) //左孩子标‘0’HCodeTable[i].code[k] = '0';elseHCodeTable[i].code[k] = '1' ; //右孩子标‘1’k++;child = parent; //迭代parent = HTree[child].parent;}HCodeTable[i].code[k] = '\0';Reverse(HCodeTable[i].code); //将编码字符逆置}}时间复杂度为O(n)(4)生成编码串将输入的字符串的每一个字符与编码表比较void Huffman::Encode(char *d)//编码,d为编码后的字符串{char *s = str;while(*s!='\0'){for (int i=0;i<n;i++)if (*s == HCodeTable[i].data ){strcat(d, HCodeTable[i].code);break;}s++;}}时间复杂度为O(n)(5)解码:算法过程:从根到叶子---自顶向下基于huffman树存储数组,从根结点开始,依据输入待解码串s中码字0或1,分别向左或右跟踪至叶子结点,叶子结点对应的字符(见码表),即为解码得到的字符;只要s串为结束,重复上述过程void Huffman::Decode(char* s, char *d) //解码,s为编码串,d为解码后的字符串{while(*s!='\0'){int parent = 2*n-2; //根结点在HTree中的下标while (HTree[parent].LChild!=-1) //如果不是叶子结点{if (*s=='0')parent = HTree[parent].LChild;elseparent = HTree[parent].RChild;s++;}*d = HCodeTable[parent].data;d++;}}时间复杂度为O(n)2.3 其他(1)哈夫曼树的输出是以凹入表示法来实现的,具体算法如下:void Huffman::Print(int i, int m){if (HTree[i].LChild == -1)cout<<setfill(' ')<<setw(m+1)<<l[i]<<setfill('-')<<setw(10-m)<<'\n';else{cout<<setfill(' ')<<setw(m+1)<<HTree[i].weight<<setfill('-')<<setw(10-m)<<'\n';Print(HTree[i].LChild,m+1);Print(HTree[i].RChild,m+1);}}(2)统计字符頻数时,利用字符的ASCII码进行计数统计,调用了cin.get()函数3. 程序运行程序框图:程序源代码:#include <iostream>#include <iomanip>using namespace std;struct HNode{int weight; //结点权值int parent; //双亲指针int LChild; //左孩子指针int RChild ; //右孩子指针};struct HCode{char data;char code[100];};class Huffman{private:HNode* HTree; //Huffman树HCode* HCodeTable; //Huffman编码表char str[1024]; //输入的原始字符串char l[256]; //叶子节点对应的字符int a[256]; //记录每个出现的字符的个数public:int n; //叶子节点数void Init(); //初始化void CreateHTree(); //创建huffman树void CreateCodeTable(); //创建编码表void PrintTable();void Encode(char *d); //编码void Decode(char *s, char *d); //解码void Print(int i,int m); //打印Huffman树void SelectMin( HNode *hTree,int n, int &i1, int &i2);//找出最小的两个权值void Reverse(char* s); //逆序void Compare(char*d); //比较压缩大小~ Huffman(); //析构};void Huffman::Init(){int nNum[256]= {0}; //记录每一个字符出现的次数int ch = cin.get();int i=0;while((ch!='\r') && (ch!='\n')){nNum[ch]++; //统计字符出现的次数str[i++] = ch; //记录原始字符串ch = cin.get(); //读取下一个字符}str[i]='\0';n = 0;for ( i=0;i<256;i++){if (nNum[i]>0) //若nNum[i]==0,字符未出现{l[n] = (char)i;a[n] = nNum[i];n++;}}}void Huffman::CreateHTree(){HTree = new HNode [2*n-1]; //根据权重数组a[0..n-1] 初始化Huffman 树for (int j = 0; j < n; j++){HTree[j].weight = a[j];HTree[j].LChild = HTree[j].RChild = HTree[j].parent = -1;}int x,y;for (int i = n; i < 2*n-1; i++) //开始建Huffman树{SelectMin( HTree, i, x, y); //从1~i中选出两个权值最小的结点HTree[x].parent = HTree[y].parent = i;HTree[i].weight = HTree[x].weight+ HTree[y].weight;HTree[i].LChild = x;HTree[i].RChild = y;HTree[i].parent = -1;}}void Huffman::SelectMin( HNode *hTree,int n, int &i1, int &i2 ){int i;//找一个比较值的起始值for(i=0; i<n; i++) //找i1{ if(hTree[i].parent==-1 ){ i1=i; break; }}i++;for( ; i<n; i++) //找i2{ if(hTree[i].parent==-1 ){ i2=i; break; }}if(hTree[i1].weight>hTree[i2].weight) //i1指向最小的{ int j=i2; i2=i1; i1 = j; }//开始找最小的两个i++;for( ; i<n; i++){ if(hTree[i].parent==-1&& hTree[i].weight < hTree[i1].weight ){ i2=i1; i1 = i; }else if( hTree[i].parent==-1&& hTree[i].weight < hTree[i2].weight){ i2=i; }}}void Huffman::Print(int i, int m){if (HTree[i].LChild == -1)cout<<setfill(' ')<<setw(m+1)<<l[i]<<setfill('-')<<setw(10-m)<<'\n'; else{cout<<setfill('')<<setw(m+1)<<HTree[i].weight<<setfill('-')<<setw(10-m)<<'\n';Print(HTree[i].LChild,m+1);Print(HTree[i].RChild,m+1);}}void Huffman::CreateCodeTable(){HCodeTable = new HCode[n]; //生成编码表for (int i=0;i<n;i++){HCodeTable[i].data = l[i];int child = i; //孩子结点编号int parent = HTree[i].parent; //当前结点的父结点编号int k=0;while(parent!=-1){if (child==HTree[parent].LChild) //左孩子标‘0’HCodeTable[i].code[k] = '0';elseHCodeTable[i].code[k] = '1' ; //右孩子标‘1’k++;child = parent; //迭代parent = HTree[child].parent;}HCodeTable[i].code[k] = '\0';Reverse(HCodeTable[i].code); //将编码字符逆置}}void Huffman::PrintTable(){for (int i=0;i<n;i++)cout<<HCodeTable[i].data<<'\t'<<HCodeTable[i].code<<endl;}void Huffman::Encode(char *d)//编码,d为编码后的字符串{char *s = str;while(*s!='\0'){for (int i=0;i<n;i++)if (*s == HCodeTable[i].data ){strcat(d, HCodeTable[i].code);break;}s++;}}void Huffman::Decode(char* s, char *d) //解码,s为编码串,d为解码后的字符串{while(*s!='\0'){int parent = 2*n-2; //根结点在HTree中的下标while (HTree[parent].LChild!=-1) //如果不是叶子结点{if (*s=='0')parent = HTree[parent].LChild;elseparent = HTree[parent].RChild;s++;}*d = HCodeTable[parent].data;d++;}}void Huffman::Reverse(char* s)//换序{char ch;int len = strlen(s);for (int i=0;i<len/2;i++){ch = s[i];s[i] = s[len-i-1];s[len-i-1] = ch;}}void Huffman::Compare(char*d)//比较压缩大小{cout<<"编码前:"<<strlen(str)*8<<"bit"<<endl;cout<<"编码后:"<<strlen(d)<<"bit"<<endl;}Huffman::~ Huffman()//析构函数{delete []HTree;delete []HCodeTable;}void main(){Huffman HFCode;char d[1024]={0};char s[1024]={0};cout<<"请输入要编码的字符串:";HFCode.Init();HFCode.CreateHTree();HFCode.CreateCodeTable();HFCode.Encode(d);HFCode.Decode(d,s);int m;cout<<"欢迎使用\n"<<"1.打印哈夫曼树\n"<<"2.打印哈夫曼编码表\n"<<"3.打印编码\n"<<"4.打印解码\n"<<"5.压缩比"<<endl;while(1){cin>>m;switch(m){case 1:{HFCode.Print(2*HFCode.n-2,1);break;}case 2:{HFCode.PrintTable( );break;}case 3:{cout<<"编码结果:"<<d<<endl;break;}case 4:{cout<<"解码结果:"<<s<<endl;break;}case 5:{pare(d);}}}}运行结果:4. 总结在编程时,最开始在字符统计时出现了空格无法统计的问题,后来用cin.get()函数进行统计。