c++实验8 哈夫曼编码-译码器
哈夫曼编码-译码器
此次实验的注释解析多加不少---若对小伙伴们有帮助 希望各位麻烦点个关注 多谢
1.哈夫曼树构造算法为:
(1)由给定的n个权值{w1,w2,…,wn}构造n棵只有根结点的二叉树,从而得到一个二叉树森林F={T1,T2,…,Tn}。
(2)在二叉树森林F中选取根结点的权值最小和次小的两棵二叉树作为新的二叉树的左右子树构造新的二叉树,新的二叉树的根结点权值为左右子树根结点权值之和。
(3)在二叉树森林F中删除作为新二叉树左右子树的两棵二叉树,将新二叉树加入到二叉树森林F中。
(4)重复步骤(2)和(3),当二叉树森林F中只剩下一棵二叉树时,这棵二叉树就是所构造的哈夫曼树。
2.编程实战:
[问题描述](设计性实验)
哈夫曼树很易求出给定字符集及其概率(或频度)分布的最优前缀码。哈夫曼编码正是一种应用广泛且非常有效的数据压缩技术。该技术一般可将数据文件压缩掉20%至90%,其压缩效率取决于被压缩文件的特征。
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时,降低传输成本。但是,这要求在发送端通过一个编码系统对待传送电文须预先编码,在接收须将传送来的数据进行译码。请自行设计实现一个具有初始化、编码、译码、输入/输出等功能的哈夫曼码的编码/译码系统。并实现以下报文的编码和译码:“this program is my favorite”。
[测试数据]
某通信的电文字符集为26个英文字母及空格字符,其出现频度如下表所示:
1、设计思想描述
(1) 问题分析。
先对输入的字符串进行操作
再利用haffman编码进行存储转译
实现节省传输成本
(2) 哈夫曼树中各结点的结构描述(提示:图示)。
(3) 哈夫曼树的存储结构描述(提示:分析采用顺序存储还是利用链式存储等)。
顺序存储
2、主要算法设计与实现
[要求]
1、利用类模板来实现。
2、类中各成员函数要求给出自行设计的算法步骤描述及主要设计思想。
3、给出类中各成员函数算法设计实现。
4、对自行设计的各算法进行评估(提示:时间复杂度、空间复杂度等)。
答:
Haffman(int weight[], int n); //构造函数 时间复杂度O(n)
Code* HaffmanCode(int n,Code* haffCode,char* c);//将haffman进行编码 时间复杂度O(n^2)
ps:此处就计算所得的 时间复杂度来看有待改善
void encoding(int n,Code* haffCode); //输出权值与编码 时间复杂度O(n)
void decoding(int n,Code* haffCode); //输出译文 时间复杂度O(n)
3、代码实现
结构体构造
HaffNode-结点结构、Code-编码的数据元素结构
struct HaffNode //哈夫曼树的结点结构 { int weight; //权值 int flag; //标记 int parent; //双亲结点下标 int leftChild; //左孩子下标 int rightChild; //右孩子下标 }; struct Code //存放哈夫曼编码的数据元素结构 { int bit[MaxN]; //数组 int start; //编码的起始下标 int weight; //字符的权值 char data; };
Haffman类构造
#include <iostream> using namespace std; class Haffman { HaffNode *hn;//= new HaffNode[2*n+1]; Code *code;//=new Code[n]; int n; public: Haffman(int weight[], int n); //构造函数 时间复杂度O(n) Code* HaffmanCode(int n,Code* haffCode,char* c);//将haffman进行编码 时间复杂度O(n^2) void encoding(int n,Code* haffCode); //输出权值与编码 时间复杂度O(n) void decoding(int n,Code* haffCode); //输出译文 时间复杂度O(n) }; Haffman::Haffman(int weight[], int n) //建立叶结点个数为n权值为weight的哈夫曼树haffTree { hn= new HaffNode[2*n+1]; int j, m1, m2, x1, x2; //哈夫曼树haffTree初始化。n个叶结点的哈夫曼树共有2n-1个结点 for(int i = 0; i < 2 * n - 1 ; i++) { if(i < n) hn[i].weight = weight[i]; else hn[i].weight = 0; hn[i].parent = 0; hn[i].flag = 0; hn[i].leftChild = -1; hn[i].rightChild = -1; } for(int i = 0;i < n-1;i++) { m1 = m2 = MaxValue; x1 = x2 = 0; for(j = 0; j < n+i;j++) { if(hn[j].weight < m1 && hn[j].flag == 0) { m2 = m1; //m2放权值第2小的权值 x2 = x1; //m2放权值第2小的下标 m1 = hn[j].weight; //更新m1 x1 = j; //更新x1,下标 } else if(hn[j].weight < m2 && hn[j].flag == 0) { m2 = hn[j].weight; x2 = j; } } //将找出的两棵权值最小的子树合并为一棵子树 hn[x1].parent = n+i; hn[x2].parent = n+i; hn[x1].flag = 1; hn[x2].flag = 1; hn[n+i].weight =hn[x1].weight+hn[x2].weight; hn[n+i].leftChild = x1; hn[n+i].rightChild = x2; } } //哈夫曼编码算法如下: Code* Haffman::HaffmanCode(int n,Code* haffCode,char* c) //由n个结点的哈夫曼树haffTree构造哈夫曼编码haffCode { int child,parent; code=new Code[n]; //求n个叶结点的哈夫曼编码 for(int i = 0; i < n; i++) { code->start = n-1; //不等长编码的最后一位为n-1 code->weight = hn[i].weight; //取得编码对应权值的字符 child = i; parent = hn[child].parent; //由叶结点向上直到根结点 while(parent != 0) { if(hn[parent].leftChild == child) code->bit[code->start] = 0; //左孩子结点编码0 else code->bit[code->start] = 1; //右孩子结点编码1 code->start--; child = parent; parent = hn[child].parent; } //保存叶结点的编码和不等长编码的起始位 for(int j = code->start+1; j < n; j++) haffCode[i].bit[j] = code->bit[j]; haffCode[i].start = code->start; haffCode[i].weight = code->weight; //保存编码对应的权值 haffCode[i].data=c[i]; //存储原始数据 } return haffCode; } void Haffman::encoding(int n,Code* haffCode) { for(int i = 0; i < n; i++) { cout << "Weight = " << haffCode[i].weight << " Code = "; for(int j = haffCode[i].start+1; j < n; j++) cout << haffCode[i].bit[j]; cout<<endl; } } void Haffman::decoding(int n,Code* haffCode) { for(int i = 0; i < n; i++) cout<<haffCode[i].data; cout<<endl; }
主函数
#include <iostream> #include <stdlib.h> const int MaxValue = 10000; //初始设定的权值最大值 const int MaxBit = 4; //初始设定的最大编码位数 const int MaxN = 30; //初始设定的最大结点个数 #include"Haffman.h" using namespace std; int main() { int n; //weight[]-权重(频率)数组 int weight[] = {186,64,13,22,32,103,21,15,47,57,1,5,32,20,57,63,15,1,48,51,80,23,8,18,1,16,1}; //a[]-按顺序存放各个字母 char a[27]={' ','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}; char b[30]; //存放输入字符串 int c[30]; //存放对应b数组的权值 cout<<"请输入需要编码英文的字符串:"; cin.getline(b,30); //将输入的字符串存储在b数组中 //将c数组权值与b数组字符串进行对应 for(n=0;b[n]!='\0';n++) { for(int l=0;l<27;l++) { if(a[l]==b[n]) { c[n]=weight[l]; break; } } } if(n > MaxN) { cout << "定义的n越界,修改MaxN! " << endl; exit(0); } //生成haffCode数组用作存储 编码后的数据 Code* haffCode=new Code[n]; Haffman hm(c, n); //调用生成Haffman树 Code* co=hm.HaffmanCode(n,haffCode,b);//得到编码后数组co cout<<"编码后:"<<endl; hm.encoding(n,co); //对co进行遍历输出 cout<<"---------------------------------------"<<endl; cout<<"译码为:"<<endl; hm.decoding(n,co); //对编码数组操作得到译码 return 0; }
结果: