哈夫曼编码

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

虽然是一个看起来简单的哈夫曼,但写起来也查阅了很多资料,比如数据难以树状打印,最后无可奈何选择了横向输出。还有文件操作的一些问题,开始保存时我直接将 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')    //输入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="\0";
187         tr[i].code="\0";
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=="\0" && 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]!='\0';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 }

 

posted @ 2019-07-07 22:12  残阳逆流  阅读(167)  评论(0编辑  收藏  举报