基于顺序表哈夫曼树

基于顺序表创建赫夫曼树

说明:赫夫曼树在信息传输上有很多的用途,刚刚学习二叉树,就遇上了赫夫曼,在学习算法的时候学到了不少的的东西。

代码实现:

  1  // 哈弗曼节点数据结构
  2  struct HuffmanNode //数据结构的设计是本赫夫曼的一大败笔,我居然用了里面的很多东西我居然用了指针。
  3 {
  4      int weight;
  5      char data;
  6     HuffmanNode * leftChild,*rightChild,*parent;
  7     HuffmanNode():leftChild(NULL),rightChild(NULL),parent(NULL){}
  8     HuffmanNode( int elem,HuffmanNode * left=NULL,
  9         HuffmanNode * right=NULL,
 10         HuffmanNode * pr=NULL):weight(elem),leftChild(left),rightChild(right){}
 11 };
 12 
 13  // 哈弗曼类
 14  class Huffman
 15 {
 16  private:
 17      char ** HuffmanCode;
 18  public:
 19     HuffmanNode * HF;
 20     ~Huffman()
 21     {
 22         delete HF;
 23         delete [] HuffmanCode;
 24     }
 25      void CreateHuffmanTree( int n);
 26      void TranslateCode( char * code, int sz, int n);
 27      void Safe( char * text, int n);
 28  protected:
 29      void Select( int n, int &s1, int &s2);
 30 };
 31 
 32  // 打印哈夫曼树
 33  void ShowRHuffman(HuffmanNode* HF,  int n);
 34  void ShowLHuffman(HuffmanNode * HF,  int n);
 35  void ShowHuffman(HuffmanNode * HF,  int n);
 36 
 37 
 38 #include <iostream>
 39  using  namespace std;
 40 #include  " Huffman.h "
 41 #include <fstream>
 42 
 43  char ** HuffmanCode;
 44 
 45 
 46  void Space( int n)
 47 {
 48      for ( int i =  0; i < n; i++) cout <<  "   ";
 49 }
 50 
 51  void ShowRHuffman(HuffmanNode* HF,  int n)
 52 {
 53      if (!HF)
 54     {
 55         cout << endl;
 56          return;
 57     }
 58      else
 59     {
 60         {
 61             ShowRHuffman(HF->rightChild,n+ 5);
 62             Space(n);
 63             cout << HF->weight << "   ";
 64             cout << HF->data;
 65             ShowLHuffman(HF->leftChild,n+ 5); 
 66         }
 67     }
 68 }
 69 
 70  void ShowLHuffman(HuffmanNode * HF,  int n)
 71 {
 72      if (!HF)
 73     {
 74         cout << endl;
 75          return;
 76     }
 77      else
 78     {
 79         {
 80             ShowLHuffman(HF->leftChild,n+ 5);
 81             Space(n);
 82             cout << HF->weight << "   ";
 83             cout << HF->data;
 84             ShowRHuffman(HF->rightChild,n+ 5);
 85         }
 86     }
 87 }
 88 
 89  void ShowHuffman(HuffmanNode * HF,  int n)
 90 {
 91      if (!HF)
 92     {
 93         cout << endl;
 94          return;
 95     }
 96      else
 97     {
 98         ShowRHuffman(HF->rightChild ,n+ 4);
 99         cout << HF->weight << "   ";
100         cout <<HF->data;
101         ShowLHuffman(HF->leftChild ,n+ 4);
102     }
103 }
104 
105  void Huffman::Select( int n, int &s1, int &s2)
106 {
107      int i; // 纯粹是计数
108      s1= 0; // 跟踪第一个目标
109      s2= 1; // 跟踪第二个目标
110 
111     HuffmanNode * p ; // 跟踪哈弗曼节点
112      p = HF;
113      int fst;
114      int snd;
115 
116      // 找出第一个和第二个父节点非NULL的节点,也就是尚未处理的节点
117      i= 0;
118      while(p->parent)
119     {
120         i++;
121         p++;
122     }
123     fst = p->weight;
124     s1 = i;
125     p++;
126     i++;
127 
128      while(p->parent)
129     {
130         i++;
131         p++;
132     }
133     snd = p->weight ;
134     s2= i;
135 
136      // 循环前的数据准备
137      p++;
138      int indexp = s2+ 1;
139 
140      if(fst<=snd) // fst始终是权重比较大的节点,在进入循环之前进行调换
141      {
142         swap(fst,snd);
143         swap(s1,s2);
144     }
145      for(i=indexp;i<=n;i++,p++,indexp++)
146     {
147          if(!p->parent)
148         {
149              if(p->weight<fst) 
150             {        
151                 fst = p->weight;
152                 s1 = indexp;
153             }
154              if(fst<snd) // fst始终是权重比较大的节点
155              {
156                 swap(fst,snd);
157                 swap(s1,s2);
158             }
159         }
160     }
161 }
162 
163  void Huffman::CreateHuffmanTree( int n)
164 {
165      int i;
166      int m = n* 2- 1; // 需要的空间为n*2-1
167       char test;
168 
169      if(n<= 1) return; // 如果是1,啥都别干了
170 
171       // 从文件当中读取权重和字符
172      ifstream fs( " hfmTree.txt ",ios:: in);
173 
174     HuffmanNode * p;
175     HF =  new HuffmanNode[m];
176     p = HF;
177     
178      /* 读取过程,收获>>重载符会忽略空
179      格和换行符,当然,在遇到这些的时候
180      读取也会停下来 */
181      for(i= 0; i<n ;i++)
182     {
183         fs. get(p->data);
184         fs. get(test);
185         fs>>p->weight ;
186         fs. get(test);
187          // cout << p->weight<<" ";
188          p++;
189     }
190     fs.close();
191 
192      /* 构建哈夫曼树,因为在这里我把哈弗曼的数据结
193      设计成了指针,所以用的都是指针,其实可以更简单,
194      就是按书上的用数组的下标。
195      下面这种做法有点生硬了,掉进了链表的思路里。 */
196      int s1,s2;
197      for(i=n;i<=m- 1;i++)
198     {
199         Select(i- 1,s1,s2);     // 将较大的下标放在s1
200          HF[i].leftChild = HF+s2; // 左边较小
201          HF[i].rightChild = HF+s1; // 右边较大
202          HF[s1].parent = HF+i;
203         HF[s2].parent = HF+i;
204         (HF+i)->weight = HF[s1].weight +HF[s2].weight;
205     }
206 
207      // 打印测试数据
208       ////////////////////////
209      p = HF;
210      for(i= 0;i<m;i++)
211     {
212         cout << p->weight << "   ";
213         p++;
214     }
215     cout <<endl;
216      ////////////////////////
217 
218     HuffmanCode =  new  char* [n];
219      char * cd =  new  char[n];
220     cd[n- 1] =  0;
221 
222      /* p用来记录当前节点的父节点,q用来记录当前节点 */
223     p = HF;
224     HuffmanNode * q;
225      int start;
226 
227      for( int i= 0;i<n;i++)
228     {
229         start = n- 1; // 将start放在最后一个字符
230           for(q = HF+i,p=(HF+i)->parent ;p!=NULL;q=p,p=p->parent)
231         {
232              if(p->leftChild == q)
233                 cd[--start] =  ' 0 ';
234              else
235                 cd[--start] =  ' 1 ';
236         }
237         HuffmanCode[i] =  new  char[n-start];     // 为当前的字符译码分配存储空间
238          strcpy(HuffmanCode[i],&cd[start]);
239 
240          // 打印测试数据
241           ////////////////////////
242          cout << &cd[start] <<endl;
243          ////////////////////////
244      }
245     delete(cd);
246 }
247 
248  void Huffman::TranslateCode( char * code, int sz, int n)
249 {
250      int i; // 纯粹是计数
251 
252     HuffmanNode * p = HF+( 2*n- 2); // 指向根节点
253      cout <<  " 原文: ";
254      for(i= 0;i<sz;i++)
255     {
256          if(p->rightChild!=NULL&&p->leftChild!=NULL)
257         {
258              if(code[i]== ' 0 ')
259                 p = p->leftChild;
260              else
261                 p = p->rightChild ;        
262 
263              if(p->rightChild==NULL&&p->leftChild==NULL) 
264             {
265                 cout << p->data;
266                 p = HF+( 2*n- 2); // 解码成功,就指向根节点
267              }
268         }
269     }
270 }
271 
272  void Huffman::Safe( char * text, int n)
273 {
274     ofstream ofs;
275     ofs.open( " CodeFile.txt ",ios:: out|ios::beg);
276      int i;
277      for( int j= 0;text[j]!=NULL;j++)
278     {
279         i= 0;
280          for(;i<n;i++)
281         {
282              if((HF+i)->data == text[j])
283             {
284                 ofs << HuffmanCode[i]; // 写入CodeFile.txt文件
285                   break;
286             }
287         }
288     }
289     ofs.close();
290 }
291 
292 
293  void main()
294 {
295      /* 预先在ToBeTran.txt文件当中存入要加密的文本 */
296     fstream ofs( " hfmTree.txt ",ios:: out);
297     fstream ifs;
298 
299      int n;
300      char ch;
301      char blank;
302      int weight;
303      char  TobeCode[ 50];
304      char  Code[ 100];
305      char CodeInFile[ 50];
306 
307     cout <<  " 输入字符集的大小n: ";
308     cin >> n;
309      int count = n;
310 
311     
312      int a = getchar();
313     cout <<  " 输入字符和其对应的权值: "<<endl;
314      while(count--)
315     {
316          /* 如下操作做得那么复杂就是为了能够读懂【空格】 */
317         cin. get(ch);
318         ofs.put(ch);
319         ofs.put( '   ');
320         cin. get(blank); // 读空格
321          cin >> weight;
322         ofs << weight;
323         ofs.put( ' \n ');
324         cin. get(blank); // 读空格
325      }
326     ofs.close();
327     ofs.clear();
328 
329     Huffman HFtest;
330     HFtest.CreateHuffmanTree(n);
331 
332     ifs.open( " ToBeTran.txt ",ios:: in|ios::beg);
333     ifs >> TobeCode;
334     cout <<  " 从ToBeTran文件当中读取等待加密的文本: "<<TobeCode<<endl;
335     ifs.close();
336     ifs.clear();
337 
338      /* 在与一个文件绑定之后,调用close来关闭流;但关闭
339      流不能改变流内部的状态,如果想用同一个文件流来
340      关联多个文件的话,最好读另一个文件之前调用clear来
341      清楚流的状态 */
342     HFtest.Safe(TobeCode,n);
343 
344     
345     ifs.open( " CodeFile.txt ",ios:: in);
346     ifs >> CodeInFile;
347     cout <<  " 译码: "<<CodeInFile<<endl;
348     ifs.close();
349     ifs.clear();
350 
351     ifs.open( " CodeFile.txt ",ios:: in);
352     ifs >> Code;
353     HFtest.TranslateCode(Code,strlen(Code),n);
354     ::ShowHuffman(HFtest.HF+( 2*n- 2), 0);
355 
356     ifs.close();  

357 }

你可能感兴趣的:(哈夫曼树)