哈夫曼编/译码器
来源:互联网 发布:淘宝连衣裙女装 编辑:程序博客网 时间:2024/06/04 08:38
【问题描述】
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。
【基本要求】
一个完整的系统应具有以下功能:
(1)1:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
(2)E:编码(Encoding)。利用以建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
(3)D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
(4)P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。
(5)T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。
【测试数据】
(1)利用教科书例6-2中的数据调试程序。 ’
(2)用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAM IS MY FAVORITE”。
【实现提示】
(1)编码结果以文本方式存储在文件CodeFile中。
(2)用户界面可以设计为“菜单”方式:显示上述功能符号,再加上“Q”,表示退出运行Quit。请用户键入一个选择功能符。此功能执行完毕后再显示此菜单,直至某次用户选择了“Q”为止。 ,
(3)在程序的一次执行过程中,第一次执行I,D或C命令之后,哈夫曼树已经在内存了,不必再读入。每次执行中不一定执行I命令,因为文件himTree可能早已建好。
(1):初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
(2)E:编码(Encoding)。利用以建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
(3)D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
(4)P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。
(5)T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。
输入的形式为:用户按照屏幕提示,选择相应的操作。在数据录入时,先输入字符,再输入空格或回车,再输入权值。
输出形式为:在屏幕上打印编码或原来的字符。 //输出结果可以是文件
程序执行的命令包括:
(1)数据录入 (2)数据输出
测试数据:
1. 概要设计
2. 抽象数据类型:
ADT BinaryTree{
数据对象:D:D具有相同特性的数据元素的集合
基本操作:
InitBiTree(&T)
操作结果:构造二叉树T。
DestroyBiTree(&T)
初始条件:二叉树存在。
操作结果:销毁二叉树T。
CreatBitree(&T,definition);
初始条件:definition给出二叉树的定义
操作结果:按definition构造二叉树T。
Value(T,e)
初始条件:二叉树存在,e是T中某个节点
操作结果:返回e的值
ClearBiTree(&T)
初始条件:二叉树T存在
操作结果:若T为空二叉树,则返回TRUE,否则FALSE.
Root(&T)
初始条件:二叉树存在;
操作结果:返回T的根。
LeftChild(T,e)
初始条件:二叉树存在,e是T中的一个节点。
操作结果:返回e的左孩子,若e无左孩子,则返回空。
RightChild(T,e)
初始条件:二叉树存在,e是T中的一个节点。
操作结果:返回e的右孩子,若e无右孩子,则返回空。
Leftsibling(T,e)
初始条件:二叉树T存在,e是T中的某个节点
操作结果:返回e的左兄弟,若e是T的左孩子或无左兄弟,这返回空。
Ringtsibling(T,e)
初始条件:二叉树T存在,e是T中的某个节点
操作结果:返回e的右兄弟,若e是T的右孩子或无右兄弟,这返回空。
}ADT BinaryTree
2. 主要程序模块:本程序包含三个模块
1)主程序模块:
void main()
{
初始化;
do
{
接受命令;
处理命令;
}while (命令=“退出”)
}
2)节点结构的单元模块——定义树的节点结构。
3)二叉树的单元模块——实现哈夫曼树的抽象数据类型;
各模块的之间的调用关系如下:
主程序模块
节点的单元模块
二叉树的单元模块
3. 详细设计
1.节点的详细定义
typedef struct{
char ch;
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
2.找出权最小的节点
void select(HuffmanTree HT,int n,int &s1,int &s2){ //找出权值最小的两个编号
int i,min1,min2;
min1=min2=MAXVALUE;
for(i=1;i<=n;++i){
if(min1>HT[i].weight&&HT[i].parent==0){
min2=min1;
s2=s1;
min1=HT[i].weight;
s1=i;
}
else
if(min2>HT[i].weight&&HT[i].parent==0){
min2=HT[i].weight;
s2=i;
}
}
}//select
3. 哈夫曼树的详细定义:
void CreateHuffman(HuffmanTree &HT,int &n){ //构造赫夫曼树HT
int m,i,s1,s2;
do{
cout<<"请输入字符个数:";
cin>>n;
if(n<=1)
cout<<"输入错误!请重新输入!"<<endl;
}while(n<=1);
m=2*n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
cout<<"请输入字符和其对应的权值(字符 权值)"<<endl;
for(i=1;i<=n;++i){
cin>>HT[i].ch>>HT[i].weight;
HT[i].parent=0;
HT[i].lchild=HT[i].rchild=0;
}
for(i=n+1;i<=m;++i){
HT[i].weight=0;
HT[i].parent=0;
HT[i].lchild=HT[i].rchild=0;
}
for(i=n+1; i<=m; i++){ //构造赫夫曼树
select(HT,i-1,s1,s2);//从HT[1..i-1]中选择parent为0且weight最小的两个结点,其序号为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;
}
}//CreateHuffman
4.编码的详细定义
void EnCoding(HuffmanTree HT,HuffmanCode HC,int n){ //将字符串编码
int i,j;
char PCode[MAXCODE];
cout<<"请输入字符串:";
do{
cin>>PCode;
cout<<PCode;
cout<<"编码为:";
for(i=0;PCode[i]!='/0';++i){
for(j=1;j<=n;++j){
if(HT[j].ch==PCode[i]){
cout<<HC[j];
break;
}
}
if(j==n+1){
cout<<endl<<"没找到字符"<<PCode[i]<<endl;
cout<<"请重新输入需要编码的字符串:";
break;
}
}
cout<<endl;
}while(j==n+1&&PCode[i]!='/0');
}//EnCoding
5.译码的详细定义
void DeCoding(HuffmanTree HT,int n){ //译码
char DCode[MAXCODE];
int i,j;
cout<<"请输入二进制编码:";
cin>>DCode;
cout<<DCode<<"译码为";
for(i=0,j=2*n-1;DCode[i]!='/0';++i){
if(DCode[i]=='0')
j=HT[j].lchild;
else
j=HT[j].rchild;
if(!HT[j].lchild&& !HT[j].rchild){ //进入叶子结点
cout<<HT[j].ch;
j=2*n-1; //重新从树根出发进行译码
}
}
cout<<endl;
}//DeCoding
5.销毁树的详细定义
void Destory(HuffmanTree &HT,HuffmanCode &HC,int n){ //销毁指针,释放存储空间
int i;
for(i=0;i<n;i++)
free(HC[i]);
free(HC);
free(HT);
}//Destroy
6.密码表输出的详细定义
void PrintCode(HuffmanTree HT,HuffmanCode HC,int n){ //输出每个字符所对应的编码
int i;
for(i=1;i<=n;++i)
cout<<HT[i].ch<<" "<<HC[i]<<endl;
}//PrintCode
7.主函数和其它函数的伪码算法
int main()
{
//主函数
initialization();//初始化
do
{
readcommand(cmd);//读入命令
interpret(cmd);//执行命令
}while (cmd!=’q’ && cmd!=’Q’)
}
void initialization()
{
……………….
}
测试结果
测试输入:
输出:
程序代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct
{
char ch;
int weight;
int selected;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef struct S
{
char c;
struct S * next;
}S,*Sentence;
typedef char * * HuffmanCode;
typedef struct TreeNode
{
struct TreeNode * lchild;
struct TreeNode * rchild;
char code;
char data;
}TreeNode,*Tree;
void Select(HuffmanTree &HT,int position,int *s1,int *s2)
{
int i,j,t;
int p,q;
int first,second;
for(i=1;i<=position;i++)
if(HT[i].selected==0)
{
first=HT[i].weight;
break;
}
for(j=i+1;j<=position;j++)
if(HT[j].selected==0)
{
second=HT[j].weight;
break;
}
if(first>second)
{
t=first;
first=second;
second=t;
}
for(i=j+1;i<=position;i++)
{
if(HT[i].selected==0)
{
t=HT[i].weight;
if(t<first)
{
second=first;
first=t;
}
else if(t<second)
{
second=t;
}
}
}
for(i=1;i<=position;i++)
{
if(HT[i].selected==0&&HT[i].weight==first)
{
HT[i].selected=1;
p=i;
break;
}
}
for(i=1;i<=position;i++)
{
if(HT[i].selected==0&&HT[i].weight==second)
{
HT[i].selected=1;
q=i;
break;
}
}
if(p>q)
{
t=q;
q=p;
p=t;
}
*s1=p;
*s2=q;
return;
}
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int * &w,int n)
{
int m,i,j,s1,s2,start,f;
char * cd;
HuffmanTree p;
m=2*n-1;
if(n<=1)
return;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(p=HT+1,i=1;i<=n;i++,p++,w++)
{
p->weight=*(w+1);
p->selected=p->lchild=p->rchild=p->parent=0;
}
for(;i<=m;i++,p++)
p->selected=p->parent=p->lchild=p->rchild=0;
for(i=n+1;i<=m;i++)
{
Select(HT,i-1,&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哈弗曼树表:/n");
printf(" 编号 权重 双亲 左孩子 右孩子/n");
for(p=HT+1,i=1;i<=m;i++,p++)
printf("%7d%7d%7d%7d%7d/n",i,p->weight,p->parent,p->lchild,p->rchild);
printf("/n");
HC=(HuffmanCode)malloc((n+1)*sizeof(char *));
cd=(char *)malloc(n*sizeof(char));
cd[n-1]='/0';
for(i=1;i<=n;i++)
{
start=n-1;
for(j=i,f=HT[i].parent;f!=0;j=f,f=HT[f].parent)
{
if(HT[f].lchild==j)
cd[--start]='0';
else
cd[--start]='1';
}
HC[i]=(char *)malloc((n-start)*sizeof(char));
strcpy(HC[i],&cd[start]);
}
free(cd);
printf("/n哈弗曼编码HC:/n");
for(i=1;i<=n;i++)
{
printf("%d: %s/n",i,HC[i]);
}
printf("/n");
}
void CodeInput(int n,int* &w,char* &code)
{
int i;
w=(int *)malloc((n+1)*sizeof(int));
code=(char *)malloc((n+1)*sizeof(char));
printf("请输入字符以及权值:/n");
for(i=1;i<=n;i++)
{
fflush(stdin);
scanf("%c %d",&code[i],&w[i]);
}
}
void Encoding(Sentence &sentence,char* &code,HuffmanCode &HC,int n)
{
char c,t,*pp;
int i;
Sentence p,q;
sentence=(Sentence)malloc(sizeof(S));
q=sentence;
q->next=NULL;
printf("请输入待编码的字符串:/n");
fflush(stdin);
while(scanf("%c",&c)!=EOF&&c!=10)
{
for(i=1;i<=n;i++)
{
t=code[i];
if(c==t)
{
pp=HC[i];
break;
}
}
for(i=0;pp[i]!='/0';i++)
{
p=(Sentence)malloc(sizeof(S));
p->c=pp[i];
q->next=p;
p->next=NULL;
q=p;
}
}
p=sentence->next;
printf("/n编码后的语句:/n");
while(p!=NULL)
{
printf("%c",p->c);
p=p->next;
}
printf("/n");
}
void Decoding(Sentence &sentence,char* &code,HuffmanCode &HC,Tree &root,int n)
{
Tree p,q;
Sentence r;
int i,j;
char c,*t;
r=sentence->next;
root=(Tree)malloc(sizeof(TreeNode));
root->lchild=NULL;
root->rchild=NULL;
for(i=1;i<=n;i++)
{
t=HC[i];
q=root;
for(j=0;j<strlen(HC[i]);j++)
{
c=t[j];
if(q->lchild!=NULL)
{
if(q->lchild->code==c)
{
q=q->lchild;
}
else if(q->rchild!=NULL)
{
q=q->rchild;
}
else
{
p=(Tree)malloc(sizeof(TreeNode));
p->lchild=NULL;
p->rchild=NULL;
p->code=c;
if(t[j+1]!='/0')
p->data='n';
else
p->data=code[i];
q->rchild=p;
q=p;
}
}
else{
p=(Tree)malloc(sizeof(TreeNode));
p->lchild=NULL;
p->rchild=NULL;
p->code=c;
if(t[j+1]!='/0')
p->data='n';
else
p->data=code[i];
q->lchild=p;
q=p;
}
}
}
q=root;
printf("/n解码后的语句:/n");
while(r!=NULL)
{
c=r->c;
if(q->lchild!=NULL)
{
if(q->lchild->code==c)
{
q=q->lchild;
if(q->lchild==NULL)
{
printf("%c",q->data);
q=root;
}
}
else
{
q=q->rchild;
if(q->lchild==NULL)
{
printf("%c",q->data);
q=root;
}
}
}
else
{
printf("%c",q->data);
q=root;
}
r=r->next;
}
printf("/n/n");
return;
}
void print(HuffmanTree &HT,int i,int space)
{
int j;
if(i)
{
print(HT,HT[i].rchild,space+5);
for(j=0;j<=space;j++)
printf(" ");
printf("%d/n",HT[i].weight);
print(HT,HT[i].lchild,space+5);
}
return ;
}
void main()
{
HuffmanTree HT;
HuffmanCode HC;
Tree root;
int *w,n;
char *code;
Sentence sentence;
printf("请输入字符集大小n:/n");
scanf("%d",&n);
CodeInput(n,w,code);
HuffmanCoding(HT,HC,w,n);
Encoding(sentence,code,HC,n);
Decoding(sentence,code,HC,root,n);
printf("/n哈弗曼树/n");
print(HT,2*n-1,0);
}