基于顺序表创建赫夫曼树
说明:赫夫曼树在信息传输上有很多的用途,刚刚学习二叉树,就遇上了赫夫曼,在学习算法的时候学到了不少的的东西。
代码实现:
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();
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 }
更多请访问:http://daoluan.net