哈夫曼编译码器

#include<iostream>
#include<fstream>
#include<string>

using namespace std;

typedef struct HuffmanNode{//结点结构
 int weight;
 int parent,lchild,rchild;
}*HfmNode;

struct HuffmanTree{//哈弗曼树
 HfmNode Node;
 char *Info;//存储字符,也可放在结点结构里定义
 int LeafNum;//叶结点数量
};

HuffmanTree T;//连接各模块变量
/****************初始化(建立哈夫曼树)函数********************/

void Initialization()        //初始化
{
 int WeightNum;
 int i,j,pos1,pos2,max1,max2;     //
 int choice;
    cout<<endl;
    cout<<"***************** 建树方案目录*********************"<<endl;
 cout<<"|                                                 |"<<endl;
 cout<<"|   方案1:输入N个字符和N个权值进行建树            |"<<endl;
 cout<<"|   方案2:以文档中字符和并以各字符出现的          |"<<endl;
 cout<<"|         频度作为权值进行建树                    |"<<endl;
 cout<<"***************************************************"<<endl;
lp: cout<<"选择(输入对应方案序号):";cin>>choice;
/********************建树方案1 ************************************/
 if(choice==1){

 cout<<"输入字符个数:";
 cin>>WeightNum;
 
 T.Node=new HuffmanNode[2*WeightNum-1];  //WeightNum权值对应的哈夫曼树中的结点总数为2*WeightNum-1个
 T.Info=new char[2*WeightNum-1];//实际只需要申请WeightNum-1;但为了实现要求(5)所以所有结点都由字符域
 for(i=0;i<WeightNum;i++)
 {
  cout<<"请输入第"<<i+1<<"个字符值";
 
  cin.get ();        
  T.Info[i]=cin.get ();  
  cout<<"请输入该字符的权值或频度";
  cin>>T.Node[i].weight;       //输入权值
  T.Node[i].parent=-1;      //为根结点
  T.Node[i].lchild=-1;      //无左孩子
  T.Node[i].rchild=-1;      //无右孩子
 }
 }
/***********************建树方案2*******************************************************/
 else if(choice==2)
 {
 char ch, *st,*name;
 st=new char[128];//128为ASCII码总数
 name=new char[20];
 cout<<"请输入文档名称:";cin>>name;
 cout<<endl;
 cout<<"提示:请确认此文件存在或检查文件名是否正确输入!"<<endl;
 cout<<endl;
 system("pause");
 ifstream infile(name);
 if(!infile)
 {
  cout<<"文件打开失败!"<<endl;//为什么字符个数统计与字符归类无法同时进行????
  exit(1);
 }
 i=0;
 int k=0;//统计字符种类
 while(infile.get (ch))
 {
  
  for(int j=0;j<=i;j++)
  {
   if(st[j]==ch) {break;}
   
   else if(j==i){
    st[k]=ch;
    ++k;
    break;
   }
  }
  i++;
 }
 
 infile.close(); 
 int *count;
 count=new int[k];
 for(int m=0;m<k;m++)
  count[m]=0;
 
 ifstream infile1(name);
 if(!infile1)
 {
  cout<<"文件打开失败!"<<endl;
  exit(1);
 }
 while(infile1.get (ch))//统计各字符在文档中出现的次数
 {
  
  for(int j=0;j<=k;j++)
   if(st[j]==ch) count[j]++;
 }
 

 infile1.close();
 WeightNum=k;
 
 T.Node=new HuffmanNode[2*WeightNum-1];
 T.Info=new char[2*WeightNum-1];
 for(i=0;i<WeightNum;i++)
 {   
  T.Info[i]=st[i];
  T.Node[i].weight=count[i];       //输入权值
  T.Node[i].parent=-1;      //为根结点
  T.Node[i].lchild=-1;      //无左孩子
  T.Node[i].rchild=-1;      //无右孩子
 }
delete st;
delete name;
delete count;
 }
 
 else {
  goto lp;
 }
/***************************************************************************/ 
 for(i=WeightNum;i<2*WeightNum-1;i++) //建立哈弗曼树
 {
  pos1=-1;
  pos2=-1;          //分别用来存放当前最小值和次小值的所在单元编号
  max1=32767;      //32767为整型数的最大值
  max2=32767;      //分别用来存放当前找到的最小值和次小值 
  
  for(j=0;j<i;j++)      //在跟节点中选出权值最小的两个
   if(T.Node[j].parent==-1)         //是否为根结点
    if(T.Node[j].weight<max1)    
    {
     max2=max1;           
     max1=T.Node[j].weight;     
     pos2=pos1;            //修改次小值所在单元编号
     pos1=j;               //修改最小值所在单元编号
    }
    else
     if(T.Node[j].weight<max2)     //比原最小值大但比原次小值要小
     {
      max2=T.Node[j].weight;     //存放次小值
      pos2=j;                  //修改次小值所在的单元编号
     }
     //for
     T.Node[pos1].parent=i;       //修改根节点位置
     T.Node[pos2].parent=i;
     T.Node[i].lchild=pos1;       //修改儿子节点位置
     T.Node[i].rchild=pos2;
     T.Node[i].parent=-1;             //表示新结点应该是根结点
     T.Node[i].weight=T.Node[pos1].weight+T.Node[pos2].weight;
 }
 T.LeafNum=WeightNum;
 
 ofstream outfile("hfmTree.dat");
 if(!outfile)
 {
  cout<<"打开文件失败!"<<endl;
  return;
 }
 outfile.write((char*)&WeightNum,sizeof(WeightNum));  //写入字符个数
 for(i=0;i<WeightNum;i++)         //把各字符信息写入文件
 
  outfile.write((char*)&T.Info[i],sizeof(T.Info[i]));
  
 
 for(i=0;i<2*WeightNum-1;i++)        //把个节点内容写入文件
 
  outfile.write((char*)&T.Node[i],sizeof(T.Node[i]));
  
 
 outfile.close();
 
 cout<<"已建立哈夫曼树!"<<endl;

}
/****************编码函数********************/
void Encoding(){

 if(T.Node==NULL)       //哈夫曼树不在内存,从文件hfmTree中读入
 {
  ifstream infile0;        //以二进制方式打开hfmTree.dat文件
  infile0.open("hfmTree.dat",ios::binary|ios::in);
  if(infile0.fail())      
  {
   cout<<"文件打开失败!/n";
   return;         
  }
  infile0.read((char*)&T.LeafNum,sizeof(T.LeafNum));  //读取叶子数

  T.Info=new char[T.LeafNum];
  T.Node=new HuffmanNode[2*T.LeafNum-1];

  for(int i=0;i<T.LeafNum;i++)              //读取字符信息
   infile0.read((char*)&T.Info[i],sizeof(T.Info[i]));

  for(i=0;i<2*T.LeafNum-1;i++)              //读取结点信息
   infile0.read((char*)&T.Node[i],sizeof(T.Node[i]));

  infile0.close();
 }
 char *Tree;          //用于存储需编码内容
 int i=0,k=0;
    cout<<"  _________________"<<endl;
    cout<<" | 测试数据选择:   |"<<endl;
 cout<<" |                 |"<<endl;
 cout<<" | A:另输入内容测试|"<<endl;
 cout<<" |                 |"<<endl;
 cout<<" | B:用ToBeTran文件|"<<endl;
 cout<<" |   内容测试!     |"<<endl;
 cout<<" |_________________|"<<endl;
 cout<<"你的选择(不分大小写):";
 char c;
 cin>>c;//  tag
 if(c=='A'||c=='a')                                         
 {                                                          
  string ch;                                              
  cout<<"请输入测试数据(输入完毕按两次回车):"<<endl;      
                                                          
        cin.ignore();//跳过tag 处输入的字符<--........................*//否则运行结果很意外y因为c也被添加至string中
  getline(cin,ch,'/n'); //回车键作为输入结束条件。所以输入结束时按两次回车,
                        //第一次作为分界符,第二次通知流对象cin已输入一行字符
  
  while(ch[k]!='/0')//统计输入字符个数
   k++;
  
  Tree=new char[k+1];
  k=0;
  while(ch[k]!='/0')//将输入的内容存到Tree中
  {
   Tree[k]=ch[k];
   k++;
  }
  Tree[k]='/0';
  
  
  cout<<"需编码内容为:";
  cout<<Tree<<endl;
  
 }
 
 else{
  ifstream infile1("ToBeTran.txt");
  if(!infile1)     
  {
   cout<<"文件打开失败!/n";
   return;         
  }
  char ch;
  int k=0;
 // infile1>>noskipws;
  while(infile1.get(ch))           
  {
   k++;                     //计算ToBeTran中正文长度,以便确定Tree的空间大小
  }
  infile1.close();   
  Tree=new char[k+1];
  ifstream infile2("ToBeTran.txt");
  
  k=0;
 // infile2>>noskipws;
  while(infile2.get(ch))
  {
   Tree[k]=ch;           //读取文件内容,并存到Tree中
   k++;
  }
  infile2.close();
  
  Tree[k]='/0';//结束标志
       
  cout<<"需编码内容为:";
  cout<<Tree<<endl;
 }


 ofstream outfile("CodeFile.txt");      //存储编码后的代码,并覆盖原文件
 if(T.Node==NULL)         //还未建哈夫曼树
 {
  cout<<"警告+提示:请先建树!/n";
  return;
 }
 char *code;
 code=new char[T.LeafNum];        //为所产生编码分配容量为T.LeafNum的存储空间
 k=0;                             
 while(Tree[k]!='/0')             
 {
  int j,start=0;
  for(i=0;i<T.LeafNum;i++)
   if(T.Info[i]==Tree[k])            //求出该文字所在单元的编号
    break;
   j=i;
   while(T.Node[j].parent!=-1)        //结点j非树根
   {
    j=T.Node[j].parent;             //非结点j的双亲结点
    if(T.Node[j].lchild==i)           //是左子树,则生成代码0
     code[start++]='0';
    else                       //是右子树,则生成代码1
     code[start++]='1';
     i=j;
   }

   int m=start-1;
   while(m>=0)        //存储哈弗曼编码
   {
    
    outfile<<code[m];
    m--;
   }
   k++;

 }
 
 outfile.close();
 cout<<"已编码!且编码形式内容已存到文件CodeFile.txt中!/n/n";
  delete Tree;
 delete code;
}  //Encoding
/****************译码函数********************/
void Decoding()

{

 int i=0,k=0;
 int j=T.LeafNum*2-1-1;      //从根结点开始往下搜索
 char* str;
 char ch;
 ifstream infile1("CodeFile.txt");          //利用已建好的哈夫曼树将文件CodeFile中的代码进行译码
 if(!infile1)        
 {
  cout<<"请先编码!/n";
  return;
 }
 cout<<"经译码,原内容为:";

 while(infile1.get(ch))           
 {
  k++;                     //计算CodeFile中代码长度
 }
 infile1.close();
 
 str=new char[k+1];

 ifstream infile2("CodeFile.txt");
 k=0;

 while(infile2.get(ch))
 {
  str[k]=ch;          //读取文件内容
  k++;
 }
 infile2.close();               
 str[k]='/0';         //结束标志符
 if(T.Node==NULL)         //还未建哈夫曼树
 {
  cout<<"请先编码!/n";
  return;
 }
 ofstream outfile("TextFile.txt");         //将字符形式的编码文件写入文件Textfile中
 while(str[i]!='/0')
 {
  if(str[i]=='0')
   j=T.Node[j].lchild;          //往左走
  else
   j=T.Node[j].rchild;         //往右走
  if(T.Node[j].rchild==-1)   //到达叶子结点
  {
   cout<<T.Info[j];         //输出叶子结点对应的字符
   
   outfile.put(T.Info[j]);
   j=T.LeafNum*2-1-1;               //存入文件
  }
  i++;
 }
 outfile.close();
 delete str;
 
 cout<<"/n译码成功且其内容已存到文件TextFile.txt中!/n/n";
}//Decoding
/****************印代码函数********************/

void Print1(){
 char ch;
 ifstream infile("Codefile.txt");//
 if(!infile)
 {
  cout<<"未进行编码"<<endl;
  return;
 }
 ofstream outfile("CodePrin.txt");//
 if(!outfile)
 {
  cout<<"打开失败!"<<endl;
  return;
 }
 int i=0;
 int j=T.LeafNum*2-1-1;  
 while(infile.get(ch))
 {
  cout<<ch;
  i++;
  if(i==50)
  {i=0;
  cout<<endl;
  }
  if(ch=='0')
   j=T.Node[j].lchild;          //往左走
  else
   j=T.Node[j].rchild;         //往右走
  if(T.Node[j].rchild==-1)   //到达叶子结点
  {
           //输出叶子结点对应的字符
   outfile.put(T.Info[j]);  //存入文件
   
            j=T.LeafNum*2-1-1;            
  }  
 }
 cout<<endl;
 infile.close();
 outfile.close();
 cout<<"相应的字符形式的编码文件已写入CodePrin.txt中!"<<endl;
}//Print()
/****************印哈夫曼树函数********************/
void Tree_Printing(){
 if(T.Node==NULL)         //未建立哈夫曼树
 {
  cout<<"请先建立哈夫曼树!/n";
  return;
 }
cout<<"所建立的哈夫曼树的凹入表形式为:"<<endl;
 ofstream fop("TreePrint.txt");
 cout<<"位置:权值(字符)  "<<"左孩子位置(权值)  "<<"右孩子位置(权值)/n";
 fop<<"位置:权值(字符)  "<<"左孩子位置 (权值) "<<"右孩子位置(权值)/n";
 int i;
  for(i=0;i<T.LeafNum;i++)
 {
  cout<<i<<":"<<T.Node[i].weight<<"("<<T.Info[i]<<") /n";
  fop<<i<<":"<<T.Node[i].weight<<"("<<T.Info[i]<<") /n";
 }
for(i=T.LeafNum;i<(2*T.LeafNum-1);i++)        //输出哈夫曼树
 {
  cout<<i<<":"<<T.Node[i].weight<<"(#)"<<"-------"
  <<T.Node[i].lchild<<"("<<T.Node[T.Node[i].lchild].weight<<")"<<"------"
  <<T.Node[i].rchild<<"("<<T.Node[T.Node[i].rchild].weight<<")"<<endl;

  fop<<i<<":"<<T.Node[i].weight<<"(#)"<<"------"
  <<T.Node[i].lchild<<"("<<T.Node[T.Node[i].lchild].weight<<")"<<"------"
  <<T.Node[i].rchild<<"("<<T.Node[T.Node[i].rchild].weight<<")"<<endl;
 }

}
/*

/****************哈夫曼编码表:********************/
void Print(){
 cout<<"哈夫曼编码表:"<<endl;
 
 char *code;
 code=new char[T.LeafNum];
 int i=0;
 
 while(i<T.LeafNum)
 {
  cout<<T.Info [i]<<":";
  int  j,start=0;
  int k;
  k=i;
  j=k;
  while(T.Node[j].parent!=-1)        //结点j非树根
  {
   j=T.Node[j].parent;             //非结点j的双亲结点
   if(T.Node[j].lchild==k)           //是左子树,则生成代码0
    code[start++]='0';
   else                       //是右子树,则生成代码1
    code[start++]='1';
   k=j;
  }
  for(int n=start-1;n>=0;n--)
   cout<<code[n];
  
  i++;
  cout<<endl;
 }
 

 delete code;
}
/****************操作界面函数************************************************************/
void Screen_display(){
 char ch;
 do{
 cout<<"*******************************************************************************"<<endl;
    cout<<"                          哈夫曼编码/译码系统"<<endl;
 cout<<endl;
 cout<<"                             操作指令目录"<<endl;
 cout<<endl;
 cout<<" I:初始化(建立哈夫曼树)  E:编码  D:译码  P:印代码  T:印哈夫曼树  Q:退出系统"<<endl;
    cout<<endl; 
 cout<<"版本:@ DOTA6.60A"<<endl;
  cout<<"*******************************************************************************"<<endl;
 cout<<endl;
 

  cout<<"输入相应操作的指令(不分大小写):"<<endl;
  cin>>ch;
 switch(ch)
 {
 case'I':
 case'i':cout<<"  现在进行'初始化'操作:"<<endl;Initialization();break;
    case'E':
 case'e':cout<<"  现在进行'编码'操作:  "<<endl;Encoding();break;
 case'D':
 case'd':cout<<"  现在进行'译码'操作: "<<endl;Decoding();break;
 case'P':
 case'p':cout<<"  现在进行'印代码'操作: "<<endl;Print1();break;
 case't':
 case'T':{Tree_Printing();cout<<endl;Print();break;}
 case'Q':
 case'q':cout<<"谢谢使用!"<<endl;exit(1);break;
 }

 }while(1);
 
}


/****************主函数********************/
void main(){

 Screen_display();
 

}

[ 问题描述 ] 利用哈夫曼码进行信息通讯可以大大提高信道利用率, 缩短信息传输时间,降低传输成本。但是, 这要求在发送端通过一个码系统对待传数据预先码; 在接收端将传来的数据进行译码( 复原 )。对于双工信道( 即可以双向传输信息的信道 ), 每端都需要一个完整的/译码系统。试为这样的信息收发站写一个哈夫曼码的译码系统。 [ 基本要求 ] 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小 n,及 n 个字符和 n 个权值,建立哈夫曼树, 并将它存于文件 hfmtree 中。 (2) C:码(Coding)。利用已建好的哈夫曼树(如不在内存, 则从文件 hfmtree 中读入), 对文件 tobetrans 中的正文进行码, 然后将结果存入 codefile 中。 (3) D:译码(Decoding)。利用已建好的哈夫曼树将文件 codefile 中的代码进行译码, 结果存入文件 textfile 中。 (4) P:印代码文件(Print)。将文件 codefile 以紧凑格式显示在终端上, 每行 50 个代码。同时将此字符形式的码文件写入文件 codeprint 中。 (5) T:印哈夫曼树(Tree print)。将已在内存中的哈夫曼树以直观的方式( 树或凹入表行式 )显示在终端上, 同时将此字符形式的哈夫曼树写入文件 treeprint 中。 [ 测试数据 ] (1)利用教科书 6-2 中的数据调试程序。 (2)用下表中给出的字符集和频度的实际统计数据建立哈夫曼树, 并实现以下报 文的码和译码: "THIS PROGRAM IS MY FAVORITE". 字符 A B C D E F G H I J 频度 186 64 13 22 32 103 21 15 47 57 1 字符 K L M N O P Q R S T U 频度 5 32 20 57 63 15 1 48 51 80 23 字符 V W X Y Z 频度 8 18 1 16 1 [ 实现提示 ] (1) 文件 codefile 的基类型可以设为子界型 bit=0..1。 (2) 用户界面可以设计为"菜单"方式: 显示上述功能符号, 再加上"E",表示结束运行 End, 请用户键入一个选择功能符。此功能执行完毕后再显示此菜单, 直至某次用户选择了 "E"为止。 (3) 在程序的一次执行过程中, 第一次执行 I,D 或 C 命令之后, 哈符曼树已经在内存了, 不必再读入。每次执行中不一定执行 I 命令, 因为文件 hfmtree 可能早己建好。 [ 选作内容 ] (1) 上述 codefile 的基类型实际上占用了存放一个整数的空间, 只起到示意或模拟的作用。现使 codefile 的基类型 package=integer, 把哈夫曼码紧缩到一个整型变量中去, 最大限度地利用码点存储能力, 试改写你的系统。 (2) 修改你的系统, 实现对你的系统的源程序的码和译码。( 主要是将行尾符/译码问题 ) (3) 实现各个转换操作的源/目文件, 均由用户在选择此操作时指定。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值