修改一位叫刘伟高的程序
1
// 程序名:HuffmanTree.h
2
// 程序功能:哈夫曼树类的头文件(并用其来实现编/译码)
3
4
//对应类实现文件: HuffmanTree.cpp
5
//对应主程序文件: main.cpp
6
7
8
9
#include<iostream>
10
#include<fstream>
11
#include<string>
12
using namespace std;
13
struct HuffmanNode //定义哈夫曼树各结点
14
{
15
int weight; //存放结点的权值,假设只考虑处理权值为整数的情况
16
int parent; //记录结点父亲位置,-1表示为根结点,否则表示为非根结点
17
int lchild,rchild; //分别存放该结点的左、右孩子的所在单元的编号
18
};
19
class HuffmanTree //建立哈夫曼树类
20
{
21
private:
22
HuffmanNode *Node; //哈夫曼树中结点的存储结构
23
char *Info; //用来保存各字符信息
24
int LeafNum; //树中的叶子结点总数
25
public:
26
HuffmanTree(); //构造函数
27
~HuffmanTree(); //析构函数
28
void Initialization(int WeightNum); //初始化函数:根据WeightNum个权值建立一棵哈夫曼树
29
void Encoder(); //编码函数:利用构造好的哈夫曼树对字符进行编码
30
void Decoder(); //译码函数:对二进制串进行译码
31
void Print(); //印文件函数:把已保存好的编码文件显示在屏幕
32
void TreePrinting(); //印哈夫曼树函数:将已在内存中的哈夫曼树以直观的方式显示在终端上
33
};

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

1
#include"HuffmanTree.h"
2
#include<string>
3
using namespace std;
4
5
//////////////////////////////////////////////////////////////////////////////
6
// 构造函数
7
// 函数功能:将结点指针初始化为NULL
8
// 函数参数:无
9
// 参数返回值:无
10
HuffmanTree::HuffmanTree()
11
{
12
Node=NULL; //将树结点初始化为空
13
Info=NULL; //将字符数组初始化为空
14
LeafNum=0; //将叶子数初始化为0
15
}
16
//////////////////////////////////////////////////////////////////////////////
17
// 析构函数
18
// 函数功能:将所有结点的空间释放
19
// 函数参数:无
20
// 参数返回值:无
21
HuffmanTree::~HuffmanTree()
22
{
23
delete[] Node; //释放结点空间
24
delete[] Info; //释放字符存储空间
25
}
26
//////////////////////////////////////////////////////////////////////////////
27
// 初始化函数
28
// 函数功能:从终端读入字符集大小n,以及n个字符和n个权值,
29
// 建立哈夫曼树,并将它存放在文件hfmTree中.
30
// 函数参数:int WeightNum表示代码个数
31
// 参数返回值:无
32
void HuffmanTree::Initialization(int WeightNum) //初始化
33
{
34
int i,j,pos1,pos2,max1,max2; //
35
36
Node=new HuffmanNode[2*WeightNum-1]; //WeightNum权值对应的哈夫曼树中的结点总数为2*WeightNum-1个
37
//Info=new char[2*WeightNum-1];
38
Info=new char[WeightNum];
39
for(i=0;i<WeightNum;i++)
40
{
41
cout<<"请输入第"<<i+1<<"个字符值";
42
getchar(); //丢弃字符'\t'与'\n'
43
Info[i]=getchar(); //输入一个字符,主要是考虑输入空格而采用这种形式的
44
//cin>>Info[i];
45
getchar();
46
cout<<"请输入该字符的权值或频度";
47
cin>>Node[i].weight; //输入权值
48
Node[i].parent=-1; //为根结点
49
Node[i].lchild=-1; //无左孩子
50
Node[i].rchild=-1; //无右孩子
51
}
52
53
for(i=WeightNum;i<2*WeightNum-1;i++) //表示需做WeightNum-1次合并
54
{
55
pos1=-1;
56
pos2=-1; //分别用来存放当前最小值和次小值的所在单元编号
57
max1=32767; //32767为整型数的最大值
58
max2=32767; //分别用来存放当前找到的最小值和次小值
59
60
for(j=0;j<i;j++) //在跟节点中选出权值最小的两个
61
if(Node[j].parent==-1) //是否为根结点
62
if(Node[j].weight<max1) //是否比最小值要小
63
{
64
max2=max1; //原最小值变为次小值
65
max1=Node[j].weight; //存放最小值
66
pos2=pos1; //修改次小值所在单元编号
67
pos1=j; //修改最小值所在单元编号
68
}
69
else
70
if(Node[j].weight<max2) //比原最小值大但比原次小值要小
71
{
72
max2=Node[j].weight; //存放次小值
73
pos2=j; //修改次小值所在的单元编号
74
}
75
//for
76
Node[pos1].parent=i; //修改父亲位置
77
Node[pos2].parent=i;
78
Node[i].lchild=pos1; //修改儿子位置
79
Node[i].rchild=pos2;
80
Node[i].parent=-1; //表示新结点应该是根结点
81
Node[i].weight=Node[pos1].weight+Node[pos2].weight;
82
} //for
83
LeafNum=WeightNum;
84
85
86
char ch;
87
cout<<"是否要替换原来文件(Y/N):";
88
cin>>ch;
89
if(ch=='y'||ch=='Y')
90
{
91
ofstream fop; //以二进制方式打开hfmTree.dat文件,并当重新运行时覆盖原文件
92
fop.open("hfmTree.dat",ios::out|ios::binary|ios::trunc);
93
if(fop.fail()) //文件打开失败
94
cout<<"文件打开失败!\n";
95
fop.write((char*)&WeightNum,sizeof(WeightNum)); //写入WeightNum
96
for(i=0;i<WeightNum;i++) //把各字符信息写入文件
97
{
98
fop.write((char*)&Info[i],sizeof(Info[i]));
99
flush(cout);
100
}
101
for(i=0;i<2*WeightNum-1;i++) //把个节点内容写入文件
102
{
103
fop.write((char*)&Node[i],sizeof(Node[i]));
104
flush(cout);
105
}
106
fop.close(); //关闭文件
107
}
108
cout<<"哈夫曼树已构造完成。\n";
109
}//Initialization
110
111
//////////////////////////////////////////////////////////////////////////////
112
// 编码函数
113
// 函数功能:利用已建立好的哈夫曼树(如不在内存,则从文件hfmTree中读入),
114
// 对文件ToBeTran中的正文进行编码,然后将结果代码存(传输)到文件CodeFile中.
115
// 函数参数:无
116
// 参数返回值:无
117
void HuffmanTree::Encoder()
118
{
119
if(Node==NULL) //哈夫曼树不在内存,从文件hfmTree中读入
120
{
121
ifstream fip; //以二进制方式打开hfmTree.dat文件
122
fip.open("hfmTree.dat",ios::binary|ios::in);
123
if(fip.fail()) //文件打开失败
124
{
125
cout<<"文件打开失败!\n";
126
return; //结束本函数
127
}
128
fip.read((char*)&LeafNum,sizeof(LeafNum)); //读取叶子数
129
Info=new char[LeafNum];
130
Node=new HuffmanNode[2*LeafNum-1];
131
for(int i=0;i<LeafNum;i++) //读取字符信息
132
fip.read((char*)&Info[i],sizeof(Info[i]));
133
for(i=0;i<2*LeafNum-1;i++) //读取结点信息
134
fip.read((char*)&Node[i],sizeof(Node[i]));
135
}
136
137
char *Tree; //用于存储需编码内容
138
int i=0,num;
139
char Choose; //让用户选择读取文件或重新输入需编码内容
140
cout<<"你要从文件中读取内容(1),还是重新输入(2):";
141
cin>>Choose;
142
if(Choose=='1') //读取文件ToBeTran.txt
143
{
144
ifstream fip1("ToBeTran.txt");
145
if(fip1.fail()) //文件不存在
146
{
147
cout<<"文件打开失败!\n";
148
return; //结束本函数
149
}
150
char ch;
151
int k=0;
152
while(fip1.get(ch))
153
{
154
k++; //计算CodeFile中代码长度
155
}
156
fip1.close();
157
158
Tree=new char[k+1];
159
ifstream fip2("ToBeTran.txt");
160
161
k=0;
162
while(fip2.get(ch))
163
{
164
Tree[k]=ch; //读取文件内容,并存到Tree中
165
k++;
166
}
167
fip2.close();
168
Tree[k]='\0'; //结束标志
169
cout<<"需编码内容为:";
170
cout<<Tree<<endl;
171
}//if(Choose=='1')
172
173
else //Choose!='1',重新输入
174
{
175
string tree; //用于输入需编码内容,由于string类对象可以输入任意长度,
176
//所以先利用这个对象输入,再转存在Tree中
177
178
cin.ignore();
179
cout<<"请输入需要编码的内容(可输入任意长,结束时请按2下回车):\n";
180
getline(cin,tree,'\n'); //输入任意长字符串,
181
//getline以回车('\n')作为结束符,第一次按回车表示字符串结束,第二次按回车才开始输出。
182
while(tree[i]!='\0')
183
i++;
184
num=i; //计算tree长度
185
i=0;
186
Tree=new char[num+1];
187
while(tree[i]!='\0') //将tree中的字符转存到Tree中
188
{
189
Tree[i]=tree[i];
190
i++;
191
}
192
Tree[i]='\0'; //结束标志符
193
}
194
195
ofstream fop("CodeFile.dat",ios::trunc); //存储编码后的代码,并覆盖原文件
196
i=0;
197
int k=0;
198
char *code;
199
code=new char[LeafNum]; //为所产生编码分配容量为LeafNum的存储空间
200
//因为不等长编码中最长的编码一定不会超过要求编码的字符个数
201
while(Tree[k]!='\0') //对每一个字符编码
202
{
203
int j,start=0;
204
for(i=0;i<LeafNum;i++)
205
if(Info[i]==Tree[k]) //求出该文字所在单元的编号
206
break;
207
j=i;
208
while(Node[j].parent!=-1) //结点j非树根
209
{
210
j=Node[j].parent; //非结点j的双亲结点
211
if(Node[j].lchild==i) //是左子树,则生成代码0
212
code[start++]='0';
213
else //是右子树,则生成代码1
214
code[start++]='1';\
215
i=j;
216
}
217
code[start]='\0'; //置串结束符
218
219
220
for(i=0;i<start/2;i++) //对二进制序列进行逆置
221
{
222
j=code[i];
223
code[i]=code[start-i-1];
224
code[start-i-1]=j;
225
}
226
i=0;
227
while(code[i]!='\0') //存储代码
228
{
229
fop<<code[i];
230
i++;
231
}
232
k++;
233
}
234
fop.close();
235
cout<<"已编码!且存到文件CodeFile.dat中!\n\n";
236
} //Encode
237
238
//////////////////////////////////////////////////////////////////////////////
239
// 译码函数
240
// 函数功能:利用已建好的哈夫曼树,对传输到达的CodeFile中的数据代码进行译码,
241
// 将译码结果存入文件TextFile中.
242
// 函数参数:无
243
// 参数返回值:无
244
void HuffmanTree::Decoder()
245
{
246
int i=0,k=0;
247
int j=LeafNum*2-1-1; //表示从根结点开始往下搜索
248
char* BitStr;
249
250
ifstream fip1("CodeFile.dat"); //利用已建好的哈夫曼树将文件CodeFile中的代码进行译码
251
if(fip1.fail()) //文件打开失败,还未编码
252
{
253
cout<< "请先编码!\n";
254
return;
255
}
256
cout<<"经译码,原内容为:";
257
char ch;
258
while(fip1.get(ch))
259
{
260
k++; //计算CodeFile中代码长度
261
}
262
fip1.close();
263
264
BitStr=new char[k+1];
265
ifstream fip2("CodeFile.dat");
266
k=0;
267
while(fip2.get(ch))
268
{
269
BitStr[k]=ch; //读取文件内容
270
k++;
271
}
272
fip2.close();
273
BitStr[k]='\0'; //结束标志符
274
if(Node==NULL) //还未建哈夫曼树
275
{
276
cout<<"请先编码!\n";
277
return;
278
}
279
ofstream fop("TextFile.dat"); //将字符形式的编码文件写入文件CodePrin中
280
while(BitStr[i]!='\0')
281
{
282
if(BitStr[i]=='0')
283
j=Node[j].lchild; //往左走
284
else
285
j=Node[j].rchild; //往右走
286
if(Node[j].rchild==-1) //到达叶子结点
287
{
288
cout<<Info[j]; //输出叶子结点对应的字符
289
j=LeafNum*2-1-1; //表示重新从根结点开始往下搜索
290
fop<<Info[j]; //存入文件
291
}//if、
292
i++;
293
}//while
294
fop.close();
295
296
cout<<"\n译码成功且已存到文件TextFile.dat中!\n\n";
297
}//Decoder
298
//////////////////////////////////////////////////////////////////////////////
299
// 印文件代码函数
300
// 函数功能:将文件CodeFile以紧凑格式显示在终端上,
301
// 每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。
302
// 函数参数:无
303
// 参数返回值:无
304
void HuffmanTree::Print()
305
{
306
char ch;
307
int i=1;
308
ifstream fip("CodeFile.dat"); //读取文件
309
ofstream fop("CodePrin.dat"); //存储文件
310
if(fip.fail())
311
{
312
cout<<"没有文件,请先编码!\n";
313
314
return;
315
}
316
while(fip.get(ch))
317
{
318
cout<<ch; //读取文件内容
319
fop<<ch; //存到文件中
320
if(i==50) //每行输出50个字符
321
{
322
cout<<endl;
323
i=0;
324
}
325
i++;
326
}
327
cout<<endl;
328
fip.close(); //关闭CodeFile.dat文件
329
fop.close(); //关闭CodePrin.dat文件
330
}
331
//////////////////////////////////////////////////////////////////////////////
332
// 印哈夫曼树函数
333
// 函数功能:将已在内存中的哈夫曼树以直观的方式(树或凹入表的形式)显示在终端上,
334
// 同时将此字符形式的哈夫曼树写入文件TreePrint中。
335
// 函数参数:无
336
// 参数返回值:无
337
void HuffmanTree::TreePrinting()
338
{
339
if(Node==NULL) //未建立哈夫曼树
340
{
341
cout<<"请先建立哈夫曼树!\n";
342
return;
343
}
344
int i;
345
int j=0,k=LeafNum-1;
346
string *HC;
347
HC=new string[LeafNum]; //定义存储Huffman编码字符串数组
348
for (i=0;i<LeafNum;i++)
349
{
350
char *temp=new char[100]; //实验目的,本处没有求二叉树的深度,而采用固定长度的数组存储Huffman中间编码
351
int k=100;
352
int m=i;
353
L1:if (Node[m].parent!=-1) //自定义跳转标签
354
{
355
if (Node[Node[m].parent].lchild==m)
356
{
357
temp[--k]='1';
358
}
359
else
360
{
361
temp[--k]='0';
362
}
363
m=Node[m].parent;
364
goto L1; //跳转到指定的标签L1
365
}
366
else
367
{
368
int n;
369
for (n=k;n<100;n++)
370
{
371
HC[i]=HC[i]+temp[n];
372
}
373
}
374
cout<<i+1<<"号字符的编码为:"<<HC[i]<<"\n"; //输出Huffman编码
375
}
376
}
377

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

1
// 程序名:main.cpp
2
// 程序功能:主函数源文件
3
4
#include"HuffmanTree.h"
5
#include<string.h>
6
#include<stdlib.h>
7
8
//////////////////////////////////////////////////////////////////////////////
9
// 主函数
10
//参数返回值:无
11
12
int main()
13
{
14
15
cout<<"(I) 初始化;\n";
16
cout<<"(E) 编码;\n";
17
cout<<"(D) 译码;\n";
18
cout<<"(P) 印代码文件;\n";
19
cout<<"(T) 印哈夫曼树\n";
20
cout<<"(Q) 退出\n\n";
21
HuffmanTree huftree; //定义哈夫曼树对象
22
int weight;
23
char Choose;
24
while(1)
25
{
26
cout<<"请从清单中选择一个操作(不区分大小写):";
27
cin>>Choose;
28
switch(Choose)
29
{
30
case 'I':
31
case 'i':
32
cout<<"请输入编码长度:";
33
cin>>weight;
34
huftree.Initialization(weight); //初始化哈夫曼树
35
break;
36
case 'E':
37
case 'e':
38
huftree.Encoder();
39
break;
40
case 'D':
41
case 'd':
42
huftree.Decoder();
43
break;
44
case 'P':
45
case 'p':
46
huftree.Print();
47
break;
48
case 'T':
49
case 't':
50
huftree.TreePrinting();
51
break;
52
case 'Q':
53
case 'q':
54
cout<<"\n ***********感谢使用本系统!***********\n\n";
55
system("pause"); //暂停运行
56
return 0;
57
}
58
cout<<"(I) 初始化;\n";
59
cout<<"(E) 编码;\n";
60
cout<<"(D) 译码;\n";
61
cout<<"(P) 印代码文件;\n";
62
cout<<"(T) 印哈夫曼树\n";
63
cout<<"(Q) 退出\n\n";
64
}
65
}
66

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66
