哈夫曼树实验(文本压缩与解压)

#include<iostream>
#include<fstream>
#include<iomanip>
#include<stdlib.h>
using namespace std;
# define MaxN 100//初始设定的最大结点数
# define MaxC 1000//最大编码长度 
# define ImpossibleWeight 10000//结点不可能达到的权值 
# define n 26//字符集的个数
//-----------哈夫曼树的结点结构类型定义-----------
typedef struct //定义哈夫曼树各结点
{
   int weight;//权值
   int parent;//双亲结点下标
   int lchild;//左孩子结点下标
   int rchild;//右孩子结点下标
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
typedef char**HuffmanCode;//动态分配数组存储哈夫曼编码表 
//-------全局变量-------- 
HuffmanTree HT;
HuffmanCode HC;
int *w;//权值数组 
//const int n=26;//字符集的个数 
char *info;//字符值数组 
int flag=0;//初始化标记 
//********************************************************************** 
//初始化函数
//函数功能: 从终端读入字符集大小n , 以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中
//函数参数: 
//向量HT的前n个分量表示叶子结点,最后一个分量表示根结点,各字符的编码长度不等,所以按实际长度动态分配空间
void Select(HuffmanTree t,int i,int &s1,int &s2) 
{    //s1为最小的两个值中序号最小的那个 
     int j; 

     int k=ImpossibleWeight;//k的初值为不可能达到的最大权值 
     for(j=1;j<=i;j++)
     {
          if(t[j].weight<k&&t[j].parent==0)
		  {k=t[j].weight;
		  s1=j;}
     }
     t[s1].parent=1;
     k=ImpossibleWeight;
     for(j=1;j<=i;j++)
     {
          if(t[j].weight<k&&t[j].parent==0)
		  {k=t[j].weight;
		  s2=j;}
     }
      t[s2].parent=1;
}
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int num)//w存放n个字符的权值(均>0),构造哈夫曼树HT,并求出n个字符的哈弗曼编码HC
{
   int i,m,c,s1,s2,start,f; 
   HuffmanTree p;
   char* cd;
   if(num<=1) return;
   m=2*num-1;//m为结点数,一棵有n个叶子结点的哈夫曼树共有2n-1个结点,可以存储在一个大小为2n-1的一维数组中
   HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用
   //--------初始化哈弗曼树-------
   for(p=HT+1,i=1;i<=num;i++,p++,w++) 
   {
     p->weight=*w;
     p->parent=0;
     p->lchild=0;
     p->rchild=0;
   }
   for(i=num+1;i<=m;i++,p++)
     {
       p->weight=0;
       p->parent=0;
       p->lchild=0;
       p->rchild=0;
     }
   //--------建哈夫曼树-------------
   for(i=num+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;
   } 
   //-------从叶子到根逆向求每个字符的哈弗曼编码--------
   HC=(HuffmanCode)malloc((num+1)*sizeof(char *));//指针数组:分配n个字符编码的头指针向量
   cd=(char*)malloc(n*sizeof(char*));//分配求编码的工作空间
   cd[n-1]='\0';//编码结束符
   for(i=1;i<=n;i++)//逐个字符求哈弗曼编码
   {
    
     start=n-1;//编码结束符位置
     for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//从叶子到跟逆向求哈弗曼编码
       if(HT[f].lchild==c) cd[--start]='0';//判断是左孩子还是右孩子(左为0右为1)
       else cd[--start]='1';
     HC[i]=(char*)malloc((num-start)*sizeof(char*));//按所需长度分配空间 
     int j,h;
     strcpy(HC[i],&cd[start]); 
   }
   free(cd);
}
//****************初始化函数******************
void Initialization()
{
     flag=1;//标记为已初始化 
     int i;
     w=(int*)malloc(n*sizeof(int));//为26个字符权值分配空间 
     info=(char*)malloc(n*sizeof(char));//为26个字符分配空间  
     ifstream infile("ABC.txt",ios::in);
     if(!infile)    
     {
        cerr<<"打开失败"<<endl;
        exit(1); 
     } 
     for(i=0;i<n;i++)
     {
        infile>>info[i];
        infile>>w[i];
     } 
     infile.close();
     cout<<"读入字符成功!"<<endl;
      HuffmanCoding(HT,HC,w,n);
      //------------打印编码-----------
      cout<<"依次显示各个字符的值,权值或频度,编码如下"<<endl; 
      cout<<"字符"<<setw(6)<<"权值"<<setw(11)<<"编码"<<endl; 
      for(i=0;i<n;i++)
      {
         cout<<setw(3)<<info[i];
         cout<<setw(6)<<w[i]<<setw(12)<<HC[i+1]<<endl;
      }
      //---------将建好的哈夫曼树写入文件------------
      cout<<"下面将哈夫曼树写入文件"<<endl;
      ofstream outfile("hfmTree.txt",ios::out);
      if(!outfile)    
      {
         cerr<<"打开失败"<<endl;
         exit(1); 
      } 
      for(i=0;i<n;i++,w++)
      {
         outfile<<info[i]<<" ";
         outfile<<w[i]<<" ";
         outfile<<HC[i+1]<<" ";
      }
      outfile.close();
      cout<<"已经将字符与对应的权值,编码写入根目录下文件hfmTree.txt"<<endl;
}
//*****************输入待编码字符函数************************* 
void Input()
{
     char string[100]; 
     ofstream outfile("ToBeTran.txt",ios::out);
      if(!outfile)    
      {
         cerr<<"打开失败"<<endl;
         exit(1); 
      } 
      cout<<"请输入你想要编码的字符串(字符个数应小于100),以#结束"<<endl;
      cin>>string;
      for(int i=0;string[i]!='\0';i++)
      {
		   if(string[i]=='\0') break;
         outfile<<string[i];
      }
      cout<<"获取报文成功"<<endl;
      outfile.close();
      cout<<"------"<<"已经将报文存入根目录下的ToBeTran.txt文件"<<endl;
}
//******************编码函数**************** 
void Encoding() 
{
     int i,j;
     char*string;
     string=(char*)malloc(MaxN*sizeof(char));
     cout<<"下面对根目录下的ToBeTran.txt文件中的字符进行编码"<<endl; 
     ifstream infile("ToBeTran.txt",ios::in);
     if(!infile)    
     {
        cerr<<"打开失败"<<endl;
        exit(1); 
     } 
     for(i=0;i<100;i++)
     {
		 
        infile>>string[i];
		
     } 
	 for(i=0;i<100;i++)
if(string[i]!='#') 
        cout<<string[i];
else break;
     infile.close();
     ofstream outfile("CodeFile.txt",ios::out);
     if(!outfile)    
     {
         cerr<<"打开失败"<<endl;
         exit(1); 
     } 
         for(i=0;string[i]!='#';i++)
         {
              for(j=0;j<n;j++)
              {
                   if(string[i]==info[j])
                   outfile<<HC[j+1];
              }
         }
     outfile<<'#';
     outfile.close();
     free(string);
     cout<<"编码完成------";
     cout<<"编码已写入根目录下的文件CodeFile.txt中"<<endl; 
} 
//******************译码函数**************** 
void Decoding()
{
	 int j=0,i;
     char *code;
     code=(char*)malloc(MaxC*sizeof(char));
     char*string;
     string=(char*)malloc(MaxN*sizeof(char));
     cout<<"下面对根目录下的CodeFile.txt文件中的代码进行译码"<<endl; 
     ifstream infile("CodeFile.txt",ios::in);
     if(!infile)    
     {
         cerr<<"打开失败"<<endl;
         exit(1); 
     } 
     for( i=0;i<MaxC;i++)
     {  infile>>code[i];
       	if(code[i]!='#')
        {
          cout<<code[i];
        }
		else break;
     } 
     infile.close();
     int m=2*n-1;
     for(i=0;code[i-1]!='#';i++)
     {
         if(HT[m].lchild==0)
               {
                   string[j]=info[m-1];
                   j++;
                   m=2*n-1;
                   i--;
               }
         else
           if(code[i]=='1') 
			   m=HT[m].rchild;
           else
             if(code[i]=='0')
				 m=HT[m].lchild;
     }
   string[j]='#';
     ofstream outfile("TextFile.txt",ios::out);
     if(!outfile)    
     {
         cerr<<"打开失败"<<endl;
         exit(1); 
     }
	 cout<<"的译码为------"<<endl;
     for( i=0;string[i]!='#';i++)
     {
         outfile<<string[i];
		 cout<<string[i];
     }
     outfile<<'#';
     outfile.close();
     cout<<"------译码完成------"<<endl;
     cout<<"译码结果已写入根目录下的文件TextFile.txt中"<<endl; 
     free(code);
     free(string); 
}
//*************打印编码函数****************
void Code_printing()
{
     int i; 
     char *code;
     code=(char*)malloc(MaxC*sizeof(char));
     cout<<"下面打印根目录下文件CodeFile.txt中的编码"<<endl;
     ifstream infile("CodeFile.txt",ios::in);
     if(!infile)    
     {
         cerr<<"打开失败"<<endl;
         exit(1); 
     } 
     for( i=0;i<MaxC;i++)
     {
        infile>>code[i];
		if(code[i]!='#')
        cout<<code[i];
		else break;
     } 
     infile.close();
	 cout<<endl;
     ofstream outfile("CodePrin.txt",ios::out);
     if(!outfile)    
     {
         cerr<<"打开失败"<<endl;
         exit(1); 
     }
     for(i=0;code[i]!='#';i++)
     {
         outfile<<code[i];
     }
     outfile.close();
     free(code);
     cout<<"------打印结束------"<<endl;
     cout<<"该字符形式的编码文件已写入文件CodePrin.txt中"<<endl;
}
//*************打印哈夫曼树函数****************
int numb=0;
void coprint(HuffmanTree start,HuffmanTree HT)   //start=ht+26这是一个递归算法
{      
if(start!=HT)
        {
          ofstream outfile("TreePrint.txt",ios::out);
          if(!outfile)    
          {
               cerr<<"打开失败"<<endl;
               exit(1); 
          }
          numb++;       //number=0 该变量为已被声明为全局变量
          coprint(HT+start->rchild,HT);             //递归先序遍历
          cout<<setw(5*numb)<<start->weight<<endl;
          //if(start->rchild==0) cout<<info[start-HT-1]<<endl;
          outfile<<start->weight;
          coprint(HT+start->lchild,HT);
          numb--;
          outfile.close();
        }
}
void Tree_printing(HuffmanTree HT,int num)
{
        HuffmanTree p;
        p=HT+2*num-1;                 //p=HT+26
        cout<<"下面打印赫夫曼树"<<endl;
        coprint(p,HT);             //p=HT+26
        cout<<"打印工作结束"<<endl;
}
//*************主函数**************************
int main()
{
     char choice;
     do{
         cout<<"************哈弗曼编/译码器系统***************"<<endl;
         cout<<"请选择您所需功能:"<<endl; 
         cout<<"<I>:初始化哈弗曼树"<<endl;
         cout<<"<W>:输入待编码字符串"<<endl;
         cout<<"<E>:利用已建好的哈夫曼树进行编码"<<endl;
         cout<<"<D>:利用已建好的哈夫曼树进行译码"<<endl;
         cout<<"<P>:打印代码文件"<<endl;
         cout<<"<T>:打印哈夫曼树"<<endl;
         cout<<"<Q>:退出"<<endl;
     if(flag==0)
     {
         cout<<"请先初始化哈夫曼树,输入I"<<endl;
         cout<<"<系统将从根目录下的文件ABC.txt中读出26个字母并对字母进行编码>"<<endl;
     }
     cin>>choice; 
     switch(choice)
     {
         case 'I':Initialization();break; 
         case 'W':Input();break; 
         case 'E':Encoding();break; 
         case 'D':Decoding();break; 
         case 'P':Code_printing();break; 
         case 'T':Tree_printing(HT,n);break; 
         case 'Q':;break; 
         default:cout<<"输入的命令出错,请重新输入!"<<endl;
     }
     }while(choice!='Q');
     free(w);
     free(info);
     free(HT);
     free(HC);
     system("pause");
     return 0;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
综合实验: 1. 问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站编写一个哈夫曼码的编/译码系统。 2. 基本要求 一个完整的系统应具有以下功能: (1) I:初始化(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 中。 3. 测试数据 用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAME IS MY FAVORITE”。 字符 A B C D E F G H I J K L M 频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 57 63 15 1 48 51 80 23 8 18 1 16 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值