题目4、哈夫曼(Huffman)编/译码器(限1人完成)
【问题描述】
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。首先输入一段包含27个字符(包含空格)的文字(可以存在一个文件中),然后统计出各个字符出现的次数,以每个字符出现的次数为权值构造哈夫曼树,求得哈夫曼编码。
【基本要求】
一个完整的系统应具有以下功能:
1、 O: 输入一段字符(要包含27各字符)存入文件chartexfile中,统计出各个字符出现的次数并以表格的形式存入文件charsumfile中.
例如如下表:
字符 | 空格 | 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 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
频度 | 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 |
2、I:初始化(Initialization)。从终端读入字符集大小n,n个字符及n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
3、 E:编码(Encoding)。利用以建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),
对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
4、D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
5、P:打印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。
同时将此字符形式的编码文件写入文件CodePrin中。
6、 T:打印哈夫曼树(Tree Printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入
表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。
【测试数据】
用文件charsumfile给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAM IS MY FAVORITE”。
测试数据要求:
要求使用1、全部合法数据;2、整体非法数据;3、局部非法数据。进行程序测试,以保证程序的稳定
/*
字符集的大小 : 5
字符集(字符集不能重复) : ABCDE
每个字符的权值 : 1 2 3 4 5
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<math.h>
using namespace std;
const int Max=1e5+10;
int m,n;//n是叶子结点,m是所有的节点
char txt[Max];
map<char,int>mp;
struct cmp
{
bool operator()(const pair<int,int> p1, const pair<int,int> p2)
{
return p1.first > p2.first; //second的小值优先
}
};
priority_queue<pair<int,int>,vector< pair<int,int> >,cmp > q;
struct HuffmanTree
{
int num;//标记编号
int val;//权值
char mark;//该点代表的字符
int lchild,rchild,parent;
int pos;//标记位置
int a;//在二叉树中是第几行
} HT[Max];
struct Code
{
char col[101];//编码的结果
char mark;//该点代表的字符
} HC[Max];
void CreateHT_Txt(int *cnt)//创建哈夫曼表
{
while(q.size()!=1)
{
int a,b;
a=q.top().second;
q.pop();
b=q.top().second;
q.pop();
HT[++(*cnt)].lchild=a;
HT[*cnt].rchild=b;
HT[*cnt].val=HT[a].val+HT[b].val;
HT[*cnt].num=*cnt;
HT[a].parent=*cnt;
HT[b].parent=*cnt;
q.push(make_pair(HT[*cnt].val,*cnt));
}
while(!q.empty())
q.pop();
}
void CreateHT_Mes()//创建哈夫曼的编码信息
{
char col[100];
memset(col,0,sizeof(col));
int f,p,start;
for(int i=1; i<=n; i++)
{
start=n-1;
p=f=i;
//printf("%d \n",HT[f].parent);
while(HT[f].parent)
{
f=HT[f].parent;
if(p==HT[f].lchild)
col[start--]='0';
else if(p==HT[f].rchild)
col[start--]='1';
p=f;
}
strcpy(HC[i].col,col+start+1);
HC[i].mark=HT[i].mark;
//printf("%d %s\n",i,HC[i].col);
memset(col,0,sizeof(col));
}
}
void CreateHTree()//操作二
{
memset(HT,0,sizeof(HT));
memset(HC,0,sizeof(HC));
m=n=0;
printf(" 建立哈夫曼树\n\n");
int len,cnt=0,x,flag=0;
while(1)
{
len,cnt=0,x,flag=0;
while(1)
{
printf(" 请输入字符集的大小,输入-1结束操作: ");
scanf("%d",&len);
if(len==-1)
break;
mp.clear();
getchar();
printf(" 请输入字符集: ");
fgets(txt,Max,stdin);
flag=0;
cnt=0;
for(int i=0; i<len; i++)
{
if(mp[txt[i]]==0)
{
mp[txt[i]]=++cnt;
HT[cnt].mark=txt[i];
HT[cnt].num=cnt;
}
else
{
printf(" 字符集有重复,请重新输入\n\n");
mp.clear();
memset(HT,0,sizeof(HT));
flag=1;
break;
}
}
if(flag==0)
break;
}
if(len==-1)
break;
printf(" 请输入每个字符的权值: ");
for(int i=1; i<=cnt; i++)
{
scanf("%d",&x);
q.push(make_pair(x,i));
HT[i].val=x;