zoukankan      html  css  js  c++  java
  • 哈夫曼编码

    修改一位叫刘伟高的程序

     1//        程序名:HuffmanTree.h
     2//      程序功能:哈夫曼树类的头文件(并用其来实现编/译码)
     3
     4//对应类实现文件: HuffmanTree.cpp
     5//对应主程序文件: main.cpp
     6
     7 
     8
     9#include<iostream>
    10#include<fstream>
    11#include<string>
    12using namespace std;
    13struct HuffmanNode        //定义哈夫曼树各结点
    14{
    15 int weight;        //存放结点的权值,假设只考虑处理权值为整数的情况
    16 int parent;        //记录结点父亲位置,-1表示为根结点,否则表示为非根结点
    17 int lchild,rchild;   //分别存放该结点的左、右孩子的所在单元的编号
    18}
    ;
    19class HuffmanTree     //建立哈夫曼树类
    20{
    21private:
    22 HuffmanNode *Node;      //哈夫曼树中结点的存储结构
    23 char *Info;           //用来保存各字符信息
    24 int LeafNum;          //树中的叶子结点总数
    25public:
    26 HuffmanTree();     //构造函数
    27 ~HuffmanTree();    //析构函数
    28 void Initialization(int WeightNum);   //初始化函数:根据WeightNum个权值建立一棵哈夫曼树
    29 void Encoder();           //编码函数:利用构造好的哈夫曼树对字符进行编码
    30 void Decoder();          //译码函数:对二进制串进行译码
    31 void Print();            //印文件函数:把已保存好的编码文件显示在屏幕
    32 void TreePrinting();     //印哈夫曼树函数:将已在内存中的哈夫曼树以直观的方式显示在终端上
    33}
    ;

      1#include"HuffmanTree.h"
      2#include<string>
      3using namespace std;
      4
      5//////////////////////////////////////////////////////////////////////////////
      6//  构造函数
      7//  函数功能:将结点指针初始化为NULL
      8//  函数参数:无
      9//  参数返回值:无
     10HuffmanTree::HuffmanTree()
     11{
     12 Node=NULL;          //将树结点初始化为空 
     13 Info=NULL;          //将字符数组初始化为空
     14 LeafNum=0;          //将叶子数初始化为0
     15}

     16//////////////////////////////////////////////////////////////////////////////
     17// 析构函数
     18// 函数功能:将所有结点的空间释放
     19// 函数参数:无
     20// 参数返回值:无
     21HuffmanTree::~HuffmanTree()
     22{
     23 delete[] Node;         //释放结点空间
     24 delete[] Info;         //释放字符存储空间
     25}

     26//////////////////////////////////////////////////////////////////////////////
     27//  初始化函数
     28//  函数功能:从终端读入字符集大小n,以及n个字符和n个权值,
     29//            建立哈夫曼树,并将它存放在文件hfmTree中.
     30//  函数参数:int WeightNum表示代码个数
     31//  参数返回值:无 
     32void HuffmanTree::Initialization(int WeightNum)        //初始化
     33{
     34 int i,j,pos1,pos2,max1,max2;     //
     35 
     36 Node=new HuffmanNode[2*WeightNum-1];  //WeightNum权值对应的哈夫曼树中的结点总数为2*WeightNum-1个
     37 //Info=new char[2*WeightNum-1];
     38 Info=new char[WeightNum];
     39 for(i=0;i<WeightNum;i++)
     40 {
     41  cout<<"请输入第"<<i+1<<"个字符值";
     42  getchar();           //丢弃字符'\t'与'\n'
     43  Info[i]=getchar();   //输入一个字符,主要是考虑输入空格而采用这种形式的
     44  //cin>>Info[i];
     45  getchar();
     46  cout<<"请输入该字符的权值或频度";
     47  cin>>Node[i].weight;       //输入权值
     48  Node[i].parent=-1;      //为根结点
     49  Node[i].lchild=-1;      //无左孩子
     50  Node[i].rchild=-1;      //无右孩子
     51 }

     52 
     53 for(i=WeightNum;i<2*WeightNum-1;i++//表示需做WeightNum-1次合并
     54 {
     55  pos1=-1;
     56  pos2=-1;          //分别用来存放当前最小值和次小值的所在单元编号 
     57  max1=32767;      //32767为整型数的最大值 
     58  max2=32767;      //分别用来存放当前找到的最小值和次小值  
     59
     60  for(j=0;j<i;j++)      //在跟节点中选出权值最小的两个
     61   if(Node[j].parent==-1)         //是否为根结点
     62    if(Node[j].weight<max1)     //是否比最小值要小
     63    
     64     max2=max1;            //原最小值变为次小值
     65     max1=Node[j].weight;      //存放最小值
     66     pos2=pos1;            //修改次小值所在单元编号
     67     pos1=j;               //修改最小值所在单元编号
     68    }

     69    else
     70     if(Node[j].weight<max2)     //比原最小值大但比原次小值要小
     71     {
     72      max2=Node[j].weight;     //存放次小值
     73      pos2=j;                  //修改次小值所在的单元编号
     74     }

     75    //for
     76  Node[pos1].parent=i;       //修改父亲位置
     77  Node[pos2].parent=i;
     78  Node[i].lchild=pos1;       //修改儿子位置
     79  Node[i].rchild=pos2;
     80  Node[i].parent=-1;             //表示新结点应该是根结点
     81  Node[i].weight=Node[pos1].weight+Node[pos2].weight;
     82 }
     //for
     83 LeafNum=WeightNum;
     84 
     85 
     86 char ch;
     87 cout<<"是否要替换原来文件(Y/N):";
     88 cin>>ch;
     89 if(ch=='y'||ch=='Y')
     90 {
     91 ofstream fop;   //以二进制方式打开hfmTree.dat文件,并当重新运行时覆盖原文件
     92 fop.open("hfmTree.dat",ios::out|ios::binary|ios::trunc);
     93 if(fop.fail())                     //文件打开失败
     94  cout<<"文件打开失败!\n";
     95 fop.write((char*)&WeightNum,sizeof(WeightNum));  //写入WeightNum
     96 for(i=0;i<WeightNum;i++)         //把各字符信息写入文件
     97 {
     98  fop.write((char*)&Info[i],sizeof(Info[i]));
     99  flush(cout);
    100 }

    101 for(i=0;i<2*WeightNum-1;i++)        //把个节点内容写入文件
    102 {
    103  fop.write((char*)&Node[i],sizeof(Node[i]));
    104  flush(cout);
    105 }

    106 fop.close();            //关闭文件
    107 }

    108 cout<<"哈夫曼树已构造完成。\n";
    109}
    //Initialization
    110
    111//////////////////////////////////////////////////////////////////////////////
    112//  编码函数
    113//  函数功能:利用已建立好的哈夫曼树(如不在内存,则从文件hfmTree中读入),
    114//            对文件ToBeTran中的正文进行编码,然后将结果代码存(传输)到文件CodeFile中.
    115//  函数参数:无
    116//  参数返回值:无
    117void HuffmanTree::Encoder()
    118{
    119 if(Node==NULL)       //哈夫曼树不在内存,从文件hfmTree中读入
    120 {
    121  ifstream fip;        //以二进制方式打开hfmTree.dat文件
    122  fip.open("hfmTree.dat",ios::binary|ios::in);
    123  if(fip.fail())       //文件打开失败
    124  {
    125   cout<<"文件打开失败!\n";
    126   return;          //结束本函数
    127  }

    128  fip.read((char*)&LeafNum,sizeof(LeafNum));  //读取叶子数
    129  Info=new char[LeafNum]; 
    130  Node=new HuffmanNode[2*LeafNum-1];
    131  for(int i=0;i<LeafNum;i++)              //读取字符信息
    132   fip.read((char*)&Info[i],sizeof(Info[i]));
    133  for(i=0;i<2*LeafNum-1;i++)              //读取结点信息
    134   fip.read((char*)&Node[i],sizeof(Node[i]));
    135 }

    136 
    137 char *Tree;          //用于存储需编码内容
    138 int i=0,num;
    139 char Choose;         //让用户选择读取文件或重新输入需编码内容
    140 cout<<"你要从文件中读取内容(1),还是重新输入(2):";
    141 cin>>Choose;
    142 if(Choose=='1')          //读取文件ToBeTran.txt
    143 {
    144  ifstream fip1("ToBeTran.txt");
    145  if(fip1.fail())      //文件不存在
    146  {
    147   cout<<"文件打开失败!\n";
    148   return;          //结束本函数
    149  }

    150  char ch;
    151  int k=0;
    152  while(fip1.get(ch))            
    153  {
    154   k++;                     //计算CodeFile中代码长度
    155  }
     
    156  fip1.close();  
    157 
    158  Tree=new char[k+1];
    159  ifstream fip2("ToBeTran.txt");
    160
    161  k=0
    162  while(fip2.get(ch))
    163  {
    164   Tree[k]=ch;           //读取文件内容,并存到Tree中
    165   k++;
    166  }

    167  fip2.close();
    168  Tree[k]='\0';          //结束标志
    169  cout<<"需编码内容为:";
    170  cout<<Tree<<endl;
    171 }
    //if(Choose=='1')
    172
    173 else           //Choose!='1',重新输入
    174 {
    175  string tree;         //用于输入需编码内容,由于string类对象可以输入任意长度,
    176                     //所以先利用这个对象输入,再转存在Tree中
    177  
    178  cin.ignore();
    179  cout<<"请输入需要编码的内容(可输入任意长,结束时请按2下回车):\n";
    180  getline(cin,tree,'\n');         //输入任意长字符串,
    181         //getline以回车('\n')作为结束符,第一次按回车表示字符串结束,第二次按回车才开始输出。
    182  while(tree[i]!='\0')
    183   i++;
    184  num=i;             //计算tree长度
    185  i=0;
    186  Tree=new char[num+1];
    187  while(tree[i]!='\0')       //将tree中的字符转存到Tree中
    188  {
    189   Tree[i]=tree[i];
    190   i++;
    191  }

    192     Tree[i]='\0';          //结束标志符
    193 }

    194 
    195 ofstream fop("CodeFile.dat",ios::trunc);      //存储编码后的代码,并覆盖原文件
    196 i=0;
    197 int k=0;
    198 char *code;
    199 code=new char[LeafNum];        //为所产生编码分配容量为LeafNum的存储空间
    200                               //因为不等长编码中最长的编码一定不会超过要求编码的字符个数
    201 while(Tree[k]!='\0')              //对每一个字符编码
    202 {
    203  int j,start=0;
    204  for(i=0;i<LeafNum;i++)
    205   if(Info[i]==Tree[k])            //求出该文字所在单元的编号
    206    break
    207   j=i;
    208  while(Node[j].parent!=-1)        //结点j非树根
    209  {
    210   j=Node[j].parent;             //非结点j的双亲结点
    211   if(Node[j].lchild==i)           //是左子树,则生成代码0
    212    code[start++]='0';
    213   else                       //是右子树,则生成代码1
    214    code[start++]='1';\
    215   i=j;
    216  }

    217  code[start]='\0';             //置串结束符
    218
    219  
    220  for(i=0;i<start/2;i++)           //对二进制序列进行逆置
    221  {
    222   j=code[i];
    223   code[i]=code[start-i-1];
    224   code[start-i-1]=j;
    225  }

    226        i=0;
    227  while(code[i]!='\0')        //存储代码
    228  {
    229   fop<<code[i];
    230   i++;
    231  }

    232  k++;
    233 }

    234 fop.close();
    235 cout<<"已编码!且存到文件CodeFile.dat中!\n\n";
    236}
      //Encode
    237
    238//////////////////////////////////////////////////////////////////////////////
    239//  译码函数
    240//  函数功能:利用已建好的哈夫曼树,对传输到达的CodeFile中的数据代码进行译码,
    241//            将译码结果存入文件TextFile中.
    242//  函数参数:无
    243//  参数返回值:无
    244void HuffmanTree::Decoder()
    245{
    246 int i=0,k=0;
    247 int j=LeafNum*2-1-1;      //表示从根结点开始往下搜索
    248 char* BitStr;
    249 
    250 ifstream fip1("CodeFile.dat");          //利用已建好的哈夫曼树将文件CodeFile中的代码进行译码
    251 if(fip1.fail())         //文件打开失败,还未编码
    252 {
    253  cout<<        "请先编码!\n";
    254  return;
    255 }

    256 cout<<"经译码,原内容为:";
    257 char ch;
    258 while(fip1.get(ch))            
    259 {
    260  k++;                     //计算CodeFile中代码长度
    261 }

    262 fip1.close();  
    263 
    264 BitStr=new char[k+1];
    265 ifstream fip2("CodeFile.dat");
    266 k=0;
    267 while(fip2.get(ch))
    268 {
    269  BitStr[k]=ch;          //读取文件内容
    270  k++;
    271 }

    272 fip2.close();                
    273 BitStr[k]='\0';         //结束标志符
    274 if(Node==NULL)         //还未建哈夫曼树
    275 {
    276  cout<<"请先编码!\n";
    277  return;
    278 }

    279 ofstream fop("TextFile.dat");         //将字符形式的编码文件写入文件CodePrin中
    280 while(BitStr[i]!='\0')
    281 {
    282  if(BitStr[i]=='0')
    283   j=Node[j].lchild;          //往左走
    284  else
    285   j=Node[j].rchild;         //往右走
    286  if(Node[j].rchild==-1)   //到达叶子结点
    287  {
    288   cout<<Info[j];         //输出叶子结点对应的字符
    289   j=LeafNum*2-1-1;             //表示重新从根结点开始往下搜索
    290   fop<<Info[j];               //存入文件
    291  }
    //if、
    292  i++;
    293 }
    //while
    294 fop.close();
    295 
    296 cout<<"\n译码成功且已存到文件TextFile.dat中!\n\n";
    297}
    //Decoder
    298//////////////////////////////////////////////////////////////////////////////
    299//  印文件代码函数
    300//  函数功能:将文件CodeFile以紧凑格式显示在终端上,
    301//            每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。
    302//  函数参数:无
    303//  参数返回值:无
    304void HuffmanTree::Print()
    305{
    306 char ch;
    307 int i=1;
    308 ifstream fip("CodeFile.dat");           //读取文件
    309 ofstream fop("CodePrin.dat");           //存储文件
    310 if(fip.fail())
    311 {
    312  cout<<"没有文件,请先编码!\n";
    313
    314  return;
    315 }

    316 while(fip.get(ch))
    317 {
    318  cout<<ch;            //读取文件内容
    319  fop<<ch;              //存到文件中
    320  if(i==50)        //每行输出50个字符
    321  {
    322   cout<<endl;
    323   i=0;
    324  }

    325  i++;
    326 }

    327 cout<<endl;
    328 fip.close();          //关闭CodeFile.dat文件
    329 fop.close();            //关闭CodePrin.dat文件
    330}

    331//////////////////////////////////////////////////////////////////////////////
    332//  印哈夫曼树函数
    333//  函数功能:将已在内存中的哈夫曼树以直观的方式(树或凹入表的形式)显示在终端上,
    334//            同时将此字符形式的哈夫曼树写入文件TreePrint中。
    335//  函数参数:无
    336//  参数返回值:无
    337void HuffmanTree::TreePrinting()
    338{
    339 if(Node==NULL)         //未建立哈夫曼树
    340 {
    341  cout<<"请先建立哈夫曼树!\n";
    342  return;
    343 }

    344 int i;
    345 int j=0,k=LeafNum-1;
    346 string *HC;
    347 HC=new string[LeafNum];            //定义存储Huffman编码字符串数组
    348 for (i=0;i<LeafNum;i++)
    349 {
    350     char *temp=new char[100];        //实验目的,本处没有求二叉树的深度,而采用固定长度的数组存储Huffman中间编码
    351     int k=100;
    352     int m=i;
    353L1:if (Node[m].parent!=-1)            //自定义跳转标签
    354     {
    355         if (Node[Node[m].parent].lchild==m)
    356         {
    357             temp[--k]='1';
    358         }

    359         else
    360         {
    361             temp[--k]='0';
    362         }

    363         m=Node[m].parent;
    364         goto L1;                    //跳转到指定的标签L1
    365     }

    366     else
    367     {
    368         int n;
    369         for (n=k;n<100;n++)
    370         {
    371             HC[i]=HC[i]+temp[n];
    372         }

    373     }

    374     cout<<i+1<<"号字符的编码为:"<<HC[i]<<"\n";    //输出Huffman编码
    375 }

    376}

    377

     1//        程序名:main.cpp
     2//      程序功能:主函数源文件
     3
     4#include"HuffmanTree.h"
     5#include<string.h>
     6#include<stdlib.h>
     7
     8//////////////////////////////////////////////////////////////////////////////
     9//  主函数
    10//参数返回值:无
    11
    12int main()
    13{
    14
    15 cout<<"(I) 初始化;\n";
    16 cout<<"(E) 编码;\n";
    17 cout<<"(D) 译码;\n";
    18 cout<<"(P) 印代码文件;\n";
    19 cout<<"(T) 印哈夫曼树\n";
    20 cout<<"(Q) 退出\n\n";
    21 HuffmanTree huftree;         //定义哈夫曼树对象
    22 int weight;
    23 char Choose;
    24 while(1)
    25 {
    26  cout<<"请从清单中选择一个操作(不区分大小写):";
    27  cin>>Choose;
    28  switch(Choose)
    29  {
    30  case 'I':
    31  case 'i':
    32   cout<<"请输入编码长度:";
    33   cin>>weight;
    34   huftree.Initialization(weight);      //初始化哈夫曼树
    35   break;
    36  case 'E':
    37  case 'e':
    38   huftree.Encoder();
    39   break;
    40  case 'D':
    41  case 'd':
    42   huftree.Decoder();
    43   break;
    44  case 'P':
    45  case 'p':
    46   huftree.Print();
    47   break;
    48  case 'T':
    49  case 't':
    50   huftree.TreePrinting();
    51   break;
    52  case 'Q':
    53  case 'q':
    54   cout<<"\n        ***********感谢使用本系统!***********\n\n";
    55  system("pause");    //暂停运行
    56   return 0;
    57  }

    58 cout<<"(I) 初始化;\n";
    59 cout<<"(E) 编码;\n";
    60 cout<<"(D) 译码;\n";
    61 cout<<"(P) 印代码文件;\n";
    62 cout<<"(T) 印哈夫曼树\n";
    63 cout<<"(Q) 退出\n\n";
    64 }

    65}

    66
  • 相关阅读:
    GDOI 2019 退役记
    SHOI2019 游记
    【WC2014】紫荆花之恋
    PKUWC 2019 & NOIWC2019 比赛总结 Formal Version
    WC 2019 颓废记
    VDUVyRLYJC
    Git学习
    DOM学习笔记
    python基础---->AJAX的学习
    python基础---->进程、线程及相关等
  • 原文地址:https://www.cnblogs.com/runsir/p/951607.html
Copyright © 2011-2022 走看看