北邮信通院数据结构实验报告三哈夫曼编码器之欧阳光明创编
北邮信通院数据结构实验报告三哈夫曼编码器.pptx

-1
2.2 关键算法分析 (1)计算出现字符的权值
利用 ASCII 码统计出现字符的次数,再将未出现的字符进行筛选,将出现的字符及頻 数存储在数组 a[]中。
void Huffman::Init() {
int nNum[256]= {0}; //记录每一个字符出现的次数 int ch = cin.get();
第2页
北京邮电大学信息与通信工程学 院
2
-1
-1
-1
5
-1
-1
-1
6
-1
-1
-1
7
-1
-1
-1
9
-1
-1
-1
weight lchild rchild parent
2
-1
-1
5
5
-1
-1
5
6
-1
-1
6
7
-1
-1
6
9
-1
-1
7
7
0
1
7
第3页
北京邮电大学信息与通信工程学 院
13
2
3
8
16
5
4
8
29
6
7
2、建立编码表(CreateTable):利用已经建好的赫夫曼树进行编码,并将每 个字符的编码输出。
3、编码(Encoding):根据编码表对输入的字符串进行编码,并将编码后的 字符串输出。
4、译码(Decoding):利用已经建好的赫夫曼树对编码后的字符串进行译 码,并输出译码结果。
5、打印(Print):以直观的方式打印赫夫曼树(选作) 6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫
北邮数据结构实验3哈夫曼编码

数据结构实验报告实验名称:实验3——哈夫曼编码学生姓名:班级:班内序号:学号:日期:2013年11月24日1.实验要求利用二叉树结构实现赫夫曼编/解码器。
基本要求:1、初始化(Init):能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立赫夫曼树2、建立编码表(CreateTable):利用已经建好的赫夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4、译码(Decoding):利用已经建好的赫夫曼树对编码后的字符串进行译码,并输出译码结果。
5、打印(Print):以直观的方式打印赫夫曼树(选作)6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫曼编码的压缩效果。
2. 程序分析2.1存储结构:struct HNode{char c;//存字符内容int weight;int lchild, rchild, parent;};struct HCode{char data;char code[100];}; //字符及其编码结构class Huffman{private:HNode* huffTree; //Huffman树HCode* HCodeTable; //Huffman编码表public:Huffman(void);void CreateHTree(int a[], int n); //创建huffman树void CreateCodeTable(char b[], int n); //创建编码表void Encode(char *s, string *d); //编码void Decode(char *s, char *d); //解码void differ(char *,int n);char str2[100];//数组中不同的字符组成的串int dif;//str2[]的大小~Huffman(void);};结点结构为如下所示:三叉树的节点结构:struct HNode//哈夫曼树结点的结构体{ int weight;//结点权值int parent;//双亲指针int lchild;//左孩子指针int rchild;//右孩子指针char data;//字符};示意图为:int weight int parent int lchild int rchild Char c 编码表节点结构:struct HCode//编码表结构体{char data;//字符char code[100];//编码内容};示意图为:基本结构体记录字符和出现次数:struct node{int num;char data;};示意图为:2.关键算法分析(1).初始化:伪代码:1.输入需要编译的文本内容2.将输入的内容保存到数组str1中3.统计出现的字符数目,并且保存到变量count中4.统计出现的不同的字符,存到str2中,将str2的大小存到dif中时间复杂度O(n!)(2).创建哈夫曼树算法伪代码:1.创建一个长度为2*n-1的三叉链表2.将存储字符及其权值的链表中的字符逐个写入三叉链表的前n个结点的data域,并将对应结点的孩子域和双亲域赋为空3.从三叉链表的第n个结点开始,3.1从存储字符及其权值的链表中取出两个权值最小的结点x,y,记录其下标x,y。
北邮-数据结构-哈夫曼树报告

数据结构之阳早格格创做真验报告真验称呼:哈妇曼树教死姓名:袁普班级:2013211125班班内序号:14号教号:2013210681日期:2014年12月1.真验手段战真质利用二叉树结构真止哈妇曼编/解码器.基原央供:1、初初化(Init):不妨对于输进的任性少度的字符串 s举止统计,统计每个字符的频度,并修坐哈妇曼树2、修坐编码表(CreateTable):利用已经修佳的哈妇曼树举止编码,并将每个字符的编码输出.3、编码(Encoding):根据编码表对于输进的字符串举止编码,并将编码后的字符串输出.4、译码(Decoding):利用已经修佳的哈妇曼树对于编码后的字符串举止译码,并输出译码截止.5、挨印(Print):以曲瞅的办法挨印哈妇曼树(选做)6、估计输进的字符串编码前战编码后的少度,并举止分解,计划赫妇曼编码的压缩效验.7、可采与二进造编码办法(选做)尝试数据:I love data Structure, I love Computer.I will try my best to study data Structure.提示:1、用户界里不妨安排为“菜单”办法:不妨举止接互.2、根据输进的字符串中每个字符出现的次数统计频度,对于不出现的字符一律不必编码2. 步调分解2.1 保存结构用struct结构典型去真止保存树的结面典型struct HNode{int weight; //权值int parent; //女节面int lchild; //左孩子int rchild; //左孩子};struct HCode //真止编码的结构典型{char data; //被编码的字符char code[100]; //字符对于应的哈妇曼编码};2.2 步调过程2.3算法1:void Huffman::Count()[1] 算法功能:对于出现字符的战出现字符的统计,构修权值结面,初初化编码表[2] 算法基原思维:对于输进字符一个一个的统计,并统计出现次数,构修权值数组,[3] 算法空间、时间搀纯度分解:空间搀纯度O(1),要遍历一遍字符串,时间搀纯度O(n)[4] 代码逻辑:leaf=0; //初初化叶子节面个数int i,j=0;int s[128]={0}; 用于保存出现的字符for(i=0;str[i]!='\0';i++) 遍历输进的字符串s[(int)str[i]]++; 统计每个字符出现次数for(i=0;i<128;i++)if(s[i]!=0){data[j]=(char)i; 给编码表的字符赋值weight[j]=s[i]; 构修权值数组j++;}leaf=j; //叶子节面个数即字符个数for(i=0;i<leaf;i++)cout<<data[i]<<"的权值为:"<<weight[i]<<endl;算法2:void Init();[1] 算法功能:构修哈弗曼树[2] 算法基原思维:根据哈妇曼树构修央供,采用权值最小的二个结面分离,新结面加进数组,再继承采用最小的二个结面继承构修.[3] 算法空间、时间搀纯度分解:与决于叶子节面个数,时间搀纯度O(n),空间搀纯度O(1)[4] 代码逻辑HTree=new HNode[2*leaf-1]; n2=n0-1,一同需要2n-1个结面空间for(int i=0;i<leaf;i++){HTree[i].weight=weight[i]; 给每个结面附权值HTree[i].lchild=-1; 初初化安排孩子战女节面,皆为-1HTree[i].rchild=-1;HTree[i].parent=-1;}int x,y; //用于记录二个最小权值for(int i=leaf;i<2*leaf-1;i++){Selectmin(HTree,i,x,y); 选出二个最小权值的结面HTree[x].parent=i; 女节面树坐为新修坐的结面 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; 女节面的女节面设为-1 }算法3:void Selectmin(HNode*hTree,int n,int&i1,int &i2);[1] 算法功能:从现有的结面中采用出二个最小的结面,返回其位子[2] 算法基原思维:先选出二个不构修的结面,而后背后依次比较,筛选出最小的二个结面[3] 算法空间、时间搀纯度分解:空间搀纯度O(1),要遍历所有结面,时间复纯度O(N)[4] 代码逻辑int i;for(i=0;i<n;i++) //n为目前有的结面个数,是个变更值,会有相加后的新权值加进{if(hTree[i].parent==-1) //女节面不是-1表示着那个结面还不被采用过{i1=i; 记录结面位子break;}}i++; //真止一遍for循环便加1,意为下次查找从目前位子启初查找for(;i<n;i++){if(hTree[i].parent==-1){i2=i; 记录第二个出采用过的结面编号break;}}if(hTree[i1].weight>hTree[i2].weight) 举止比较,使I1为最小的,I2为第二小的{int j=0;j=i2;i2=i1;i1=j;}i++;for(;i<n;i++) 将I1 I2 与后里的结面举止比较{if(hTree[i].parent==-1&&hTree[i].weight<hTree[i1].weight) 如果结面小于I1{i2=i1; 使I2=I1 I1=新结面i1=i;}else if(hTree[i].parent==-1&&hTree[i].weight<hTree[i2].weight){ I1《新结面《I2,使I2为新节面i2=i;}}算法4:void CreateTable();[1] 算法功能:对于出现的字符举止编码[2] 算法基原思维:根据字符正在哈妇曼树中的位子,从下到上编码,是左孩子编0,左孩子编1[3] 算法空间、时间搀纯度分解:空间搀纯度O(1),要遍历data数组,时间搀纯度0(N)[4] 代码逻辑HCodeTable=new HCode[leaf]; 新修编码结面,个数为叶子节面个数for(int i=0;i<leaf;i++){HCodeTable[i].data=data[i];int child=i; 初初化要编码的结面的位子int parent=HTree[i].parent; 初初化女结面int k=0; //统计编码个数while(parent!=-1){if(child==HTree[parent].lchild)HCodeTable[i].code[k]='0'; //左孩子标‘0’elseHCodeTable[i].code[k]='1'; //左孩子标‘1’k++;child=parent; 孩子结面上移parent=HTree[child].parent; 女节面也上移}HCodeTable[i].code[k]='\0'; //将编码反背char code[100];for(int u=0;u<k;u++)code[u]=HCodeTable[i].code[k-u-1];for(int u=0;u<k;u++)HCodeTable[i].code[u]=code[u];cout<<data[i]<<"的哈妇曼编码为:";cout<<HCodeTable[i].code<<endl;length3[i]=k; //每一个字符编码的少度,为供编码总少度干准备}算法5:void Encoding();[1] 算法功能:对于输进的字符串举止编码[2] 算法基原思维:找到每个字符对于应的编码,将编码按程序输出[3] 算法空间、时间搀纯度分解:空间搀纯度O(1),时间搀纯度0(n)[4] 代码逻辑cout<<endl<<"输进的字符串转移为哈妇曼编码为:"<<endl;for (int i=0;str[i]!='\0';i++) 遍历输进的每一个字符{for(int j=0;j<leaf;j++)if(str[i]==HCodeTable[j].data) 找到字符对于应的编码{ s1=s1+HCodeTable[j].code; 将所有编码按程序加起去cout<<HCodeTable[j].code; 输出编码 }}cout<<endl;算法6:void Decoding();[1] 算法功能:对于编码串举止解码[2] 算法基原思维:找到每段编码对于应的字符,输出字符[3] 算法空间、时间搀纯度分解:时间搀纯度0(N),空间搀纯度0(1)[4] 代码逻辑(可用真代码形貌)cout<<"解码后的字符串为: "<<endl;char *s = const_cast<char*>(s1.c_str()); 将编码字符串转移为charwhile(*s!='\0'){int parent=2*leaf-2; 女节面为末尾一个节面while(HTree[parent].lchild!=-1) //另有左子树,不可能是叶子节面{if(*s=='0') 编码为0,为左孩子parent=HTree[parent].lchild;elseparent=HTree[parent].rchild; 编码为1,为左孩子s++;}cout<<HCodeTable[parent].data; 输出字符}cout<<endl;……注意分解步调的时间搀纯度、内存申请战释搁,以及算法思维的体现.2.4 其余正在此次考查中使用了类战STL中的string,使用string不妨便当的将单个字符的编码加起去成为总的编码后的数值,再利用STL中的转移函数不妨间接将string 转移为char,便当举止解码处事.总而止之,使用STL使得编码大大的简净了.3.步调运止截止分解调试历程中逢到的问题主假如真止时有内存过失,查看后创造是数组有越界局里,那指示尔正在编写时一定要小心,特地是正在for循环条件上一定要注意范畴归纳最先正在输进字符串时尔创造间接用cin无法输进空格,正在上钩查询后找到了getline函数办理了那个问题.而后另有便是怎么样保存编码后总的那个字符串,果为每一个字符编码的少度大概,无法用char数组去保存,于是用了string 的相加函数去将所有编码加起去.末尾由于正在解码时要用char数组,又上钩查询到了string转移成char的函数办理了那个问题,真验易面也正在于怎么样找到二个最小权值去构修哈妇曼树,觅找二个最小权值的思维主假如通过一个个的比较去找到最小值,而且注意形参要用引用.通过此次真验尔体验到了stl的劣良性.另有便是编码时要注意数组的大小.再者便是有问题时不妨试着去网上查询问案.。
北邮数据结构实验报告三题目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) // 如果没有该字符,创建节点。
北邮信通院数据结构实验报告三哈夫曼编码器之欧阳引擎创编

数据结构实验报告欧阳引擎(2021.01.01)实验名称:实验三树——哈夫曼编/解码器学生姓名:班级:班内序号:学号:日期: 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 rchildparent 2-1-1-15-1-1-1 6-1-1-1 7-1-1-1 9-1-1-1weight lchild rchild parent2-1-155-1-156-1-167-1-169-1-17701713238165482967-12.2关键算法分析(1)计算出现字符的权值利用ASCII码统计出现字符的次数,再将未出现的字符进行筛选,将出现的字符及頻数存储在数组a[]中。
数据结构哈夫曼编码实验报告

数据结构哈夫曼编码实验报告【正文】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 lovedata Structure, I loveputer。
I willtrymy best tostudy data Structure、提示:1、用户界面可以设计为“菜单”方式:能够进行交互。
2、根据输入得字符串中每个字符出现得次数统计频度,对没有出现得ﻩ字符一律不用编码。
2、程序分析2、1存储结构Huffman树给定一组具有确定权值得叶子结点,可以构造出不同得二叉树,其中带权路径长度最小得二叉树称为Huffman树,也叫做最优二叉树。
weightlchildrchild parent2-1-1-15-1-1-16-1-1-17-1-1-19-1-1-1weight lchild rchildparent 2-1-155-1-156-1-167-1-169-1-17701713238165482967-12、2 关键算法分析(1)计算出现字符得权值利用ASCII码统计出现字符得次数,再将未出现得字符进行筛选,将出现得字符及頻数存储在数组a[]中。
void Huffman::Init(){ﻩintnNum[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树构成规则进行。
北邮数据结构实验报告

二、试验内容:
用编码。
利用二叉树结构实现哈夫曼编/解码器
2. 程序分析
1.初始化:能够对输入的任意长度的字符串 s 进行统计,统计每
2.1 存储结构
个字符的频度,并建立哈夫曼树。
二叉树
2.建立编码表:利用已经建好的哈夫曼树进行编码,并将每个字
魏
第1页共3页
本文格式为 Word 版,下载可任意编辑
template class BiTree { public: BiTree(); //构造函数,其前序序列由键盘输入 ~BiTree(void); //析构函数 BiNode* Getroot(); //获得指向根结点的指针 protected: BiNode *root; //指向根结点的头指针 }; //声明类 BiTree 及定义结构 BiNode Data: 二叉树是由一个根结点和两棵互不相交的左右子树构成
魏
第2页共3页
本文格式为 Word 版,下载可任意编辑
{ char data; //编码表中的字符 char code[100]; //该字符对应的编码 }; 待编码字符串由键盘输入,输入时用链表存储,链表节点为 struct Node { char character; //输入的字符 unsigned int count;//该字符的权值 bool used; //建立树的时候该字符是否使用过 Node* next; //保存下一个节点的地址 }; 示意图:
本文格式为 Word 版,下载可任意编辑
北邮数据结构实验报告
符的编码输出。 3.编码:依据编码表对输入的字符串进行编码,并将编码后的
字符串输出。
2021 级数据结构试验报告
4.译码:利用已经建好的哈夫曼树对编码后的字符串进行译码,
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构实验报告欧阳光明(2021.03.07)实验名称:实验三树——哈夫曼编/解码器学生姓名:班级:班内序号:学号:日期: 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 studydata Structure.提示:1、用户界面可以设计为“菜单”方式:能够进行交互。
2、根据输入的字符串中每个字符出现的次数统计频度,对没有出现的字符一律不用编码。
2. 程序分析2.1 存储结构Huffman树给定一组具有确定权值的叶子结点,可以构造出不同的二叉树,其中带权路径长度最小的二叉树称为Huffman树,也叫做最优二叉树。
weight lchild rchildparent 2-1-1-15-1-1-1 6-1-1-1 7-1-1-1 9-1-1-1weight lchild rchild parent2-1-155-1-156-1-167-1-169-1-17701713238165482967-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];i nt child = i; //孩子结点编号i nt parent = HTree[i].parent; //当前结点的父结点编号i nt k=0;w hile(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].cod e<<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()函数进行统计。