代码改变世界

基于顺序表哈夫曼树

2011-11-12 10:59  捣乱小子  阅读(2040)  评论(0编辑  收藏

基于顺序表创建赫夫曼树

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

代码实现:

  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 }