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

    之前帮同学做的一个哈夫曼编码的课设,有些功能还没有完善。

    虽然是一个看起来简单的哈夫曼,但写起来也查阅了很多资料,比如数据难以树状打印,最后无可奈何选择了横向输出。还有文件操作的一些问题,开始保存时我直接将 stdout 的输出目标替换成文件,简便了不少,但是后来发现在文件操作结束后,将 stdout 的目标重新设为控制台会有bug,程序结束会出现乱码,百度后用 dup 和 dup2 复制句柄解决。

    根据字母频度自动编码还没有写,这个功能先鸽了。

    短短的百行代码让我再途中也有过想放弃,我意识到一个工程是多么冗杂和困难,自己要学的还有很多,要走的路也还很长,但我一定会坚持下来的。

      1 H#include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <algorithm>
      5 #include <fstream>
      6 #include <iomanip>
      7 #include <io.h>
      8 #define MaxN 100
      9 using namespace std;
     10 
     11 typedef struct
     12 {
     13     string data;    //原字母 
     14     string code;    //对应码字 
     15     double value;    //权值 
     16     int lchild,rchild;    //左右孩子 
     17     int deep;    //深度 
     18     int flag;    //用于判断是否合并 
     19     
     20 }hufftree;
     21 
     22 class huffman
     23 {
     24     
     25     public:
     26         void create();    //创建哈夫曼树 
     27         void huffcode();    //创建哈夫曼编码 
     28         void huffdfs(int);    //深度优先遍历 
     29         void print();    //输出哈夫曼树 
     30         void printree(int,int,int);    //树状递归输出 
     31         void translate_text();    //编码 
     32         void translate_code();    //译码 
     33         void inithuff();    //初始化 
     34         void huffmerge(int,int);    //合并节点 
     35         int getmin();    //获取当前最小权值节点的下标 
     36         void changeline();    //换行 
     37     private:
     38         hufftree tr[2*MaxN];    //创建节点 
     39         int N=0;    //叶子节点数 
     40         int M=0;    //总结点数 
     41         int time=0;    //记录当前步骤
     42         //c11开始支持类内初始化,之前版本可能报错 
     43 };
     44 
     45 void huffman::create()
     46 {
     47     system("cls");    //清屏 
     48     inithuff();
     49     int step,flagg=0;
     50     do
     51     {
     52         if(flagg)
     53             cout<<"输入有误,请重新输入:";
     54         else
     55         {
     56             changeline();
     57             cout<<"1.输入待编码字母及权值"<<endl;
     58             cout<<"2.使用预设样例测试"<<endl; 
     59             changeline();
     60             cout<<"请选择:";
     61         }
     62         cin>>step;
     63         flagg=1;
     64     }
     65     while(step!=1 && step!=2);
     66     system("cls");
     67     cin.sync();    //清空缓存区 
     68     if(step==1)
     69     {
     70         cout<<"请输入待编码字母及其权值,用回车隔开(输入0结束输入)"<<endl;
     71         for(int i=0;i<MaxN;i++)
     72         {
     73             char temp[50];
     74             double val=0;
     75             cin.getline(temp,50);
     76             cin.sync();
     77             if(temp[0]=='0'&&temp[1]=='')    //输入0结束输入 
     78                 break;
     79             cin>>val;
     80             cin.sync();
     81             tr[N].data=temp;
     82             tr[N++].value=val;
     83         }
     84         M=N;
     85     }
     86     if(step==2)
     87     {
     88         string let[MaxN]={" ","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"};
     89         double val[MaxN]={0.2,0.063,0.0105,0.023,0.035,0.105,0.0221,0.011,0.047,0.054,0.001,0.003,0.029,0.021,0.059,0.0644,0.0175,0.001,0.053,0.052,0.071,0.0225,0.008,0.012,0.002,0.012,0.001};
     90         for(int i=0;i<27;i++)
     91         {
     92             tr[N].data=let[i];
     93             tr[N++].value=val[i];
     94         }
     95         M=N;
     96     }
     97     time=1;
     98     changeline();
     99     cout<<"输入完毕,共"<<N<<"组数据"<<endl;
    100     if(N!=0)
    101     {
    102         huffcode();
    103         changeline();
    104         int ifsave=0,flaag=0;
    105         do
    106         {
    107             if(flaag)
    108                 cout<<"输入有误,请重新输入:";
    109             else
    110             {
    111                 cout<<"是否保存至文件"<<endl;
    112                 cout<<"1.是"<<endl
    113                     <<"2.否"<<endl;
    114             }
    115             flaag=1;    
    116             cin>>ifsave;
    117         }while(ifsave!=1 && ifsave!=2);
    118         system("cls");
    119         if(ifsave==1)
    120         {
    121             /*-----------------------------------------------
    122             将输出对象从控制台转换到文件,保存后再回到控制台 
    123             ------------------------------------------------*/ 
    124             #define STDOUT 1
    125             int oldstdout=dup(STDOUT);
    126             FILE *fp;
    127             if(freopen("huffcode.txt","w",stdout)==NULL)
    128             {
    129                 cout<<"创建"huffcode.txt"失败"<<endl;
    130                 return;
    131             }    
    132             cout<<std::left<<setw(10)<<"字母"<<setw(10)<<"权值"<<setw(10)<<"编码"<<endl;
    133             for(int i=0;i<N;i++)
    134             {
    135                 if(tr[i].data==" ")
    136                     cout<<setw(10)<<"空格"<<setw(10)<<tr[i].value<<setw(10)<<tr[i].code<<endl;
    137                 else
    138                     cout<<setw(10)<<tr[i].data<<setw(10)<<tr[i].value<<setw(10)<<tr[i].code<<endl;
    139             }
    140             freopen("CON","w",stdout);
    141             dup2(oldstdout,STDOUT);
    142             close(oldstdout);
    143             system("cls");
    144             cout<<"已保存至"huffcode.txt""<<endl;
    145         }
    146     }
    147     
    148 }
    149 
    150 int huffman::getmin()
    151 {
    152     /*----------------------------------------------- 
    153     遍历搜索权值最小的节点,若权值相同,取深度较小者
    154     -------------------------------------------------*/ 
    155     double minval=10000;
    156     int minnum=-1,mindeep=0;
    157     for(int i=0;i<M;i++)
    158     {
    159         if(tr[i].flag==0)    //忽略已使用的节点 
    160         {
    161             if(minval>tr[i].value)
    162             {
    163                 minval=tr[i].value;
    164                 mindeep=tr[i].deep;
    165                 minnum=i;
    166             }
    167             else if(minval==tr[i].value)
    168             {
    169                 if(mindeep>tr[i].deep)
    170                 {
    171                     minval=tr[i].value;
    172                     mindeep=tr[i].deep;
    173                     minnum=i;
    174                 }
    175             }
    176         }
    177     }
    178     tr[minnum].flag=1;
    179     return minnum;
    180 }
    181 
    182 void huffman::inithuff()
    183 {
    184     for(int i=0;i<2*MaxN;i++)
    185     {
    186         tr[i].data="";
    187         tr[i].code="";
    188         tr[i].lchild=-1;
    189         tr[i].rchild=-1;
    190         tr[i].value=0;
    191         tr[i].deep=1;
    192         tr[i].flag=0;
    193     }
    194     N=0,M=0;
    195 }
    196 
    197 void huffman::huffcode()
    198 {
    199     /*----------------------------------- 
    200     获取两个权值最小节点的下标并将其合并 
    201     ------------------------------------*/ 
    202     for(int i=1;i<N;i++)
    203     {
    204         int m1=getmin(),m2=getmin();
    205         huffmerge(m1,m2);
    206     }
    207     /*----------------------------------- 
    208     找打仅剩的一个未使用的节点,即根节点 
    209     -------------------------------------*/ 
    210     for(int i=0;i<M;i++)
    211     {
    212         if(tr[i].flag==0)
    213             huffdfs(i);
    214     }
    215     cout<<"哈夫曼树建立完成"<<endl;
    216     cout<<std::left<<setw(10)<<"字母"<<setw(10)<<"权值"<<setw(10)<<"编码"<<endl;
    217     for(int i=0;i<N;i++)
    218     {
    219         if(tr[i].data==" ")
    220             cout<<setw(10)<<"空格"<<setw(10)<<tr[i].value<<setw(10)<<tr[i].code<<endl;
    221         else
    222             cout<<setw(10)<<tr[i].data<<setw(10)<<tr[i].value<<setw(10)<<tr[i].code<<endl;
    223     } 
    224 }
    225 
    226 void huffman::huffmerge(int t1,int t2)
    227 {
    228     /*--------------------- 
    229     合并权值,标明左右孩子 
    230     -----------------------*/ 
    231     tr[M].value=tr[t1].value+tr[t2].value;
    232     tr[M].deep=max(tr[t1].deep,tr[t2].deep)+1;
    233     tr[M].lchild=t1,tr[M].rchild=t2;
    234     M++;
    235 }
    236 
    237 void huffman::print()
    238 {
    239     system("cls");
    240     changeline();
    241     if(time<1)
    242         cout<<"未初始化,请先初始化再打印"<<endl;
    243     else
    244     {
    245         int maxdeep=0;
    246         for(int i=0;i<M;i++)    //找出最大深度 
    247         {
    248             if(maxdeep<tr[i].deep)
    249                 maxdeep=tr[i].deep;
    250         }
    251         for(int i=0;i<M;i++)
    252         {
    253             if(tr[i].flag==0)
    254             {
    255                 cout<<"深度  ";
    256                 for(int j=1;j<=maxdeep;j++)
    257                     cout<<j<<"   ";
    258                 cout<<endl;
    259                 printree(i,1,0);
    260             }    
    261         }
    262     }
    263     changeline();
    264     int ifsave=0,flaag=0;
    265     do
    266     {
    267         if(flaag)
    268             cout<<"输入有误,请重新输入:";
    269         else
    270         {
    271             cout<<"是否保存至文件"<<endl;
    272             cout<<"1.是"<<endl
    273                 <<"2.否"<<endl;
    274         }
    275         flaag=1;    
    276         cin>>ifsave;
    277     }while(ifsave!=1 && ifsave!=2);
    278     system("cls");
    279     if(ifsave==1)
    280     {
    281         #define STDOUT 1
    282         int oldstdout=dup(STDOUT);
    283         FILE *fp;
    284         if(freopen("hufftree.txt","w",stdout)==NULL)
    285         {
    286             cout<<"创建"hufftree.txt"失败"<<endl; 
    287             return;
    288         }
    289         int maxdeep=0;
    290         for(int i=0;i<M;i++)
    291         {
    292             if(maxdeep<tr[i].deep)
    293                 maxdeep=tr[i].deep;
    294         }
    295         for(int i=0;i<M;i++)
    296         {
    297             if(tr[i].flag==0)
    298             {
    299                 cout<<"深度  ";
    300                 for(int j=1;j<=maxdeep;j++)
    301                     cout<<j<<"   ";
    302                 cout<<endl;
    303                 printree(i,1,0);
    304             }    
    305         }
    306         freopen("CON","w",stdout);
    307         dup2(oldstdout,STDOUT);
    308         close(oldstdout);
    309         system("cls");
    310         cout<<"已保存至"hufftree.txt""<<endl;
    311     }
    312 }
    313 
    314 /*------------------------------------------- 
    315 三个参数:
    316 t为节点下标
    317 line为行数,便于控制树状输出
    318 dirct为该节点是左孩子还是右孩子,1是右,2是左 
    319 ---------------------------------------------*/ 
    320 void huffman::printree(int t,int line,int dirct)    
    321 {
    322     if(tr[t].code=="" && tr[t].value==0)  
    323     {
    324         return;
    325     }
    326     if(tr[t].rchild!=-1)
    327         printree(tr[t].rchild,line+1,1); 
    328 
    329     for(int i=0;i<line;i++)  
    330     {
    331         if(i==0)
    332             cout<<"      ";
    333         else 
    334             cout<<"|   ";
    335     }
    336     if(dirct==1)
    337         cout<<'/';
    338     if(dirct==2)
    339         cout<<'\';
    340     cout<<tr[t].value<<endl;
    341     if(tr[t].lchild!=-1)    
    342         printree(tr[t].lchild,line+1,2);    
    343 }
    344 
    345 void huffman::huffdfs(int t)    //遍历哈夫曼树,并同时给左右孩子编码 
    346 {
    347     if(tr[t].lchild!=-1)
    348     {
    349         tr[tr[t].lchild].code=tr[t].code+"0";    
    350         huffdfs(tr[t].lchild);
    351     }
    352     if(tr[t].rchild!=-1)
    353     {
    354         tr[tr[t].rchild].code=tr[t].code+"1";    
    355         huffdfs(tr[t].rchild);
    356     }    
    357 }
    358 
    359 void huffman::translate_text()
    360 {
    361     system("cls");
    362     cin.sync();
    363     cout<<"请输入待编码文本(输入回车结束):"<<endl;
    364     char text[1000]; 
    365     cin.getline(text,1000);
    366     cin.sync();
    367     if(time<1)
    368     {
    369         cout<<endl<<"未初始化,请先初始化"<<endl;
    370         return;
    371     }
    372     /*----------------------------------------------- 
    373     在叶子节点中搜索文本的每个字符,并将对应编码记录 
    374     -------------------------------------------------*/ 
    375     string ans;
    376     string st=text;
    377     for(int i=0;st[i]!='';i++)
    378     {
    379         int flag=0;
    380         for(int j=0;j<N;j++)
    381         {
    382             if(equal(tr[j].data.begin(),tr[j].data.begin()+1,st.begin()+i)==1)
    383             {
    384                 ans+=tr[j].code;
    385                 flag=1;
    386             }
    387             
    388         }
    389         if(flag==0)
    390         {
    391             cout<<"错误,文本中存在未编码字符"<<endl;
    392             return;
    393         }
    394     }
    395     system("cls");
    396     changeline();
    397     cout<<"编码完成"<<endl;
    398     cout<<"原文本:"<<endl;;
    399     cout<<text<<endl;
    400     cout<<"译文:"<<endl;
    401     cout<<ans<<endl;
    402     changeline();
    403     int ifsave=0,flaag=0;
    404     do
    405     {
    406         if(flaag)
    407             cout<<"输入有误,请重新输入:";
    408         else
    409         {
    410             cout<<"是否保存至文件"<<endl;
    411             cout<<"1.是"<<endl
    412                 <<"2.否"<<endl;
    413         }
    414         flaag=1;    
    415         cin>>ifsave;
    416     }while(ifsave!=1 && ifsave!=2);
    417     system("cls");
    418     if(ifsave==1)
    419     {
    420         #define STDOUT 1
    421         int oldstdout=dup(STDOUT);
    422         FILE *fp;
    423         if(freopen("translate_text.txt","w",stdout)==NULL)
    424         {
    425             cout<<"创建"translate_text.txt"失败"<<endl; 
    426             return;
    427         }
    428         cout<<"原文本:"<<endl;;
    429         cout<<text<<endl;
    430         cout<<"译文:"<<endl;
    431         cout<<ans<<endl;
    432         freopen("CON","w",stdout);
    433         dup2(oldstdout,STDOUT);
    434         close(oldstdout);
    435         system("cls");
    436         cout<<"已保存至"translate_text.txt""<<endl;
    437     }
    438 }
    439 
    440 void huffman::translate_code()
    441 {
    442     system("cls");
    443     cin.sync();
    444     cout<<"请输入待译码文本(输入回车结束):"<<endl;
    445     char text[100000];
    446     cin.getline(text,100000);
    447     cin.sync();
    448     if(time<1)
    449     {
    450         cout<<endl<<"未初始化,请先初始化"<<endl;
    451         return;
    452     }
    453     /*------------------------------------- 
    454     在叶子节点中搜索码字,并将对应字符记录
    455     ---------------------------------------*/ 
    456     string ans;
    457     string st=text;
    458     int len=0;
    459     while(len<st.length())
    460     {
    461         int flag=0;
    462         for(int j=0;j<N;j++)
    463         {
    464             if(equal(tr[j].code.begin(),tr[j].code.end(),st.begin()+len)==1)
    465             {
    466                 ans+=tr[j].data;
    467                 len+=tr[j].code.length();
    468                 flag=1;
    469             }
    470         }
    471         if(flag==0)
    472         {
    473             cout<<"译码错误,请检查文本"<<endl;
    474             return;
    475         }
    476     }
    477     system("cls");
    478     changeline();
    479     cout<<"原文本:"<<endl;
    480     cout<<text<<endl;
    481     cout<<"译文:"<<endl;
    482     cout<<ans<<endl;
    483     changeline();
    484     int ifsave=0,flaag=0;
    485     do
    486     {
    487         if(flaag)
    488             cout<<"输入有误,请重新输入:";
    489         else
    490         {
    491             cout<<"是否保存至文件"<<endl;
    492             cout<<"1.是"<<endl
    493                 <<"2.否"<<endl;
    494         }
    495         flaag=1;    
    496         cin>>ifsave;
    497     }while(ifsave!=1 && ifsave!=2);
    498     if(ifsave==1)
    499     {
    500         #define STDOUT 1
    501         int oldstdout=dup(STDOUT);
    502         FILE *fp;
    503         if(freopen("translate_code.txt","w",stdout)==NULL)
    504         {
    505             cout<<"创建"translate_code.txt"失败"<<endl; 
    506             return;
    507         }
    508         cout<<"原文本:"<<endl;
    509         cout<<text<<endl;
    510         cout<<"译文:"<<endl;
    511         cout<<ans<<endl;
    512         freopen("CON","w",stdout);
    513         dup2(oldstdout,STDOUT);
    514         close(oldstdout);
    515         system("cls");
    516         cout<<"已保存至"translate_code.txt""<<endl;
    517     }
    518 }
    519 
    520 void huffman::changeline()    //输出一行'-',便于查看 
    521 {
    522     for(int i=0;i<120;i++)
    523         cout<<'-';
    524 }
    525 
    526 
    527 
    528 int main()
    529 {
    530     char ch;
    531     system("color 0F");    //设置控制台为黑底白字 
    532 //    system("mode con cols=120");    //设置行宽为120 
    533     huffman huff;
    534     do
    535     {
    536         huff.changeline();
    537         cout<<std::right<<setw(55)<<"欢迎使用哈夫曼编码系统"<<endl;
    538         cout<<setw(35)<<"1:初始化"<<endl
    539             <<setw(33)<<"2:编码"<<endl
    540             <<setw(33)<<"3:译码"<<endl
    541             <<setw(41)<<"4:打印哈夫曼树"<<endl
    542             <<setw(33)<<"5:退出"<<endl;
    543         huff.changeline();
    544         cout<<"请选择(1-5):";
    545         cin >>ch;
    546         while(ch>'5' || ch<'1')
    547         {
    548             cout <<"数据输入错误,请重新选择(1-5):";
    549             cin  >>ch;
    550         }
    551         switch(ch)
    552         {
    553         case'1':
    554             huff.create();
    555             break;
    556         case'2':
    557             huff.translate_text();
    558             break;
    559         case'3':
    560              huff.translate_code();
    561             break;
    562         case'4':
    563             huff.print();
    564             break;
    565         case'5':
    566             system("cls");
    567             cout<<"欢迎再次使用"<<endl;
    568             break;
    569         }
    570     }while(ch!='5');
    571     return 0;
    572 }
  • 相关阅读:
    iptables允许FTP
    FTP服务添加用户及设置权限
    Python之异步IO&RabbitMQ&Redis
    Python之生产者&、消费者模型
    如何使用Git上传项目代码到github
    11-3 基于cookie和session的登录模块
    11-1 会话控制cookie
    11-2 会话控制session
    10-4 文件的下载
    10-3 文件的上传
  • 原文地址:https://www.cnblogs.com/lihengyang/p/11148181.html
Copyright © 2011-2022 走看看