哈夫曼编码实验报告.docx
《哈夫曼编码实验报告.docx》由会员分享,可在线阅读,更多相关《哈夫曼编码实验报告.docx(26页珍藏版)》请在冰豆网上搜索。
哈夫曼编码实验报告
哈夫曼编码实验报告:
数据结构的某项实验
①问题描述:
给定n个字符的权值数组w,根据哈夫曼编码与译码规则,实现一个哈夫曼编/译码系统(利用实验指导书上的27个字符的数据进行实验)。
②利用顺序表存储Huffman树,编码结果的存储方式采用书上的结构。
③Huffman树的构造约定如下:
根的权值较小的子树作为左子树,当权值相等时,则先生成的子树是左子树;
按照结点的生成次序选择权值较小的两棵子树构造Huffman树;
从叶子结点到根结点逆向求出每个字符的Huffman编码,不采用递归方法;
从根结点开始实现译码,要求被译码的字符数大于20个字符。
④采用文件方式存储n个权值和待翻译的二进制代码,其余数据均不采用文件存储。
序号字符权值双亲结点左孩子右孩子
1□186000
2A64000
3B13000
4C22000
5D32000
6E103000
7F21000
8G15000
9H47000
10I57000
11J1000
12K5000
13L32000
14M20000
15N57000
16O63000
17P15000
18Q1000
19R48000
20S51000
21T80000
22U23000
23V8000
24W18000
25X1000
26Y16000
27Z1000
1.实验过程与结果
完整代码:
(实验环境codeblock)
#include
#include
#include
//左0右1
typedefstruct
{
unsignedintweight;
unsignedintparent,lchild,rchild;
charc;
intlength;
}HTNode,*HuffmanTree;
typedefchar**HuffmanCode;
voidsave(intn,intw[],charcode[],charch[])
{
FILE*fp;
charfilename[20];
inti;
printf("请输入要保存的文件名称:
\n");
scanf("%s",filename);
if((fp=fopen(filename,"wb"))==NULL)//打开输出文件
{
printf("cannotopenfile:
\n");
return;
}
for(i=0;i<=n;i++)
{
if(fwrite(&w[i],sizeof(int),1,fp)!
=1)//数组向磁盘文件写入各叶子节点权值
printf("filewriteerror!
\n");
}
for(i=0;i<=n;i++)
{
if(fwrite(&ch[i],sizeof(char),1,fp)!
=1)//数组向磁盘文件写入各叶子字母代表
printf("filewriteerror!
\n");
}
for(i=0;i<=strlen(code);i++)
{
if(fwrite(&code[i],sizeof(char),1,fp)!
=1)//数组向磁盘文件写入待破解电码代码
printf("filewriteerror!
\n");
}
printf("保存文件成功!
!
!
");
fclose(fp);
}
voidread(intn,intw[],charcode[],charch[])
{
FILE*fp;
charfilename[20];
inti;
printf("请输入读入的文件名:
\n");
scanf("%s",filename);
if((fp=fopen(filename,"rb"))==NULL)//以只读方式打开二进制文件
{printf("cannotopenfile\n");
fclose(fp);
}
for(i=0;i<=n;i++)
fread(&w[i],sizeof(int),1,fp);//磁盘文件向数组读入
for(i=0;i<=n;i++)
fread(&ch[i],sizeof(char),1,fp);
for(i=0;i<=strlen(code);i++)
fread(&code[i],sizeof(char),1,fp);
printf("读入数据成功!
!
!
!
");
fclose(fp);
}
voidcreateHuffmanTree(HuffmanTree\*HT,intw[],intn,charch[])
{
intm=2*n-1;
ints1,s2,i;
\*HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(i=1;i<=n;i++)
{(\*HT)[i].weight=w[i];(\*HT)[i].lchild=0;
(\*HT)[i].parent=0;(\*HT)[i].rchild=0;
(\*HT)[i].c=ch[i];(\*HT)[i].length=0;
}
for(i=n+1;i<=m;i++)
{(\*HT)[i].weight=0;(\*HT)[i].lchild=0;
(\*HT)[i].parent=0;(\*HT)[i].rchild=0;
(\*HT)[i].c='\0';(\*HT)[i].length=0;
}
printf("\n构建哈夫曼树:
\n");
for(i=n+1;i<=m;i++)
{select(HT,i-1,&s1,&s2);(\*HT)[s1].parent=i;
(\*HT)[s2].parent=i;(\*HT)[i].lchild=s1;
(\*HT)[i].rchild=s2;
(\*HT)[i].weight=(\*HT)[s1].weight+(\*HT)[s2].weight;
}printf("\n");
}
voidselect(HuffmanTree\*HT,intn,int\*s1,int\*s2)
{inti=0,min;
for(i=1;i<=n;i++)
{if((\*HT)[i].parent==0)
{min=i;break;
}
}
for(i=1;i<=n;i++)
{if((\*HT)[i].parent==0)
{if((\*HT)[i].weight<(\*HT)[min].weight)
{min=i;
}
}
}\*s1=min;
for(i=1;i<=n;i++)
{if((\*HT)[i].parent==0&&i!
=(\*s1))
{min=i;break;
}
}
for(i=1;i<=n;i++)
{if((\*HT)[i].parent==0&&i!
=(\*s1))
{if((\*HT)[i].weight<(\*HT)[min].weight)
{min=i;
}
}\*s2=min;
}
}
//从叶子到根逆向求每个字符的哈夫曼编码
voidcreatHuffmanCode(HuffmanTreeHT,HuffmanCodeHC,intn)
{
inti,j,c;
intstart;
intf;
HC=(HuffmanCode\*)malloc((n+1)*sizeof(char\*));//分配n个字符编码的头指针向量
char\*cd=(char\*)malloc(n\*sizeof(char));//分配求编码的工作区间
cd[n-1]='\0';//从右向左逐位存放编码,先存放编码结束符
for(i=1;i<=n;i++)//逐个字符求哈夫曼编码
{start=n-1;
for(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)
{if(HT[f].lchild==c)
cd[--start]='0';
else
cd[--start]='1';
}
HC[i]=(char\*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
HT[i].length=n-start-1;//求编码长度
strcpy(HC[i],&cd[start]);
}free(cd);//释放工作空间
for(i=1;i<=n;i++)
{
printf("权值为%3d对应字母为%c的叶子节点的哈夫曼编码是%s长度为%d\n",HT[i].weight,HT[i].c,HC[i],HT[i].length);}
}
//定义编码解码函数
voidTranslatecode(HuffmanTreeHT,HuffmanCodeHC,intn,charcode[])
{inti,j,m,c,choice;
intstart;
intf;
charzcode[100];//存放输入的字母字符串
m=2*n-1;
HC=(HuffmanCode*)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
char*cd=(char*)malloc(n*sizeof(char));//分配求编码的工作区间
cd[n-1]='\0';//从右向左逐位存放编码,先存放编码结束符
for(i=1;i<=n;i++)//逐个字符求哈夫曼编码
{start=n-1;
for(c=i,f=HT[i].parent;f!
=0;c=f,f=HT[f].parent)
{if(HT[f].lchild=\=c)
cd[--start]='0';
else
cd[--start]='1';
}
HC[i]=(char\*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);
}
while
(1)
{printf("\n是否进行编译操作。
1-执行2-退出:
\n");
scanf("%d",&choice);
getchar();
if(choice=\=1)
{printf("请输入需要编码的字母字符串:
\n");
gets(zcode);
printf("编译好的二进制编码为:
\n");
for(i=0;zcode[i]!
='\0';i++)
for(j=1;j<=n;j++)
if(HT[j].c=\=zcode[i])
printf("%s",HC[j]);
}
elseif(choice=\=2)
break;
}
while
(1){printf("\n是否进行解码操作。
1-执行2-退出\n");
scanf("%d",&choice);
if(choice=\=1)
{j=0;
while(code[j]!
='\0')
{if(code[j]=\='0')
m=HT[m].lchild;
elsem=HT[m].rchild;
if(HT[m].lchild=\=0)//找到叶子节点m
{printf("%c",HT[m].c);
m=2\*n-1;//回到根节点
}j++;
}
getchar();
printf("\n原电码为:
\n");
printf("%s",code);
printf("\n请输入已破译的电文:
\n");
gets(zcode);
printf("反编译的电码为:
\n");
for(i=0;zcode[i]!
='\0';i++)
for(j=1;j<=n;j++)
if(HT[j].c=\=zcode[i])
printf("%s",HC[j]);
printf("\n判断电文输入正确否1-正确2-错误:
\n");
scanf("%d",&choice);
if(choice=\=1)
printf("\n电文输入正确!
\n");
elseprintf("\n电文输入错误!
\n");}
elseif(choice=\=2)
break;}
}
//定义获取叶子节点权值和字母的函数
voidgetcode(charch[],intw[],intN[]){
inti,j,count;
intk=1;
chartemp;
charzcode[50];//字母字符串
printf("请输入字母字符串:
\n");
scanf("%s",zcode);
for(i=0;zcode[i]!
='\0';i++)
{if(zcode[i]=='*')
continue;
temp=zcode[i];
count=0;
for(j=i;zcode[j]!
='\0';j++)
{if(temp==zcode[j])
{zcode[j]='*';
count++;
}
ch[k]=temp;//记录叶子节点代表的字母
w[k]=count;//权值为字母出现频率
}
k++;}
N[0]=k-1;}
voidmain()
{HuffmanTreeHT;
HuffmanCodeHC;//各叶子节点的哈夫曼编码
charch[30];//各叶子节点的字符代表
charcode[400];//存放输入的二进制编码字符串
intw[30];//w数组存储权值
inti,j,n,m,c;
intN[1];//当执行0号操作时,作用等价于n的值
HC=(HuffmanCode*)malloc((n+1)*sizeof(char*));
while
(1)
{printf("\n0-输入叶子节点个数(采用2号方法建树时使用)");printf("\n1-输入字母字符创建哈夫曼树");//0号操作与1号操作选择一个
printf("\n2-输入数据的权值和相应的字符代表创建哈夫曼树");
printf("\n3-输入要破解的电文密码");
printf("\n4-文件存储信息");
printf("\n5-文件读取信息");
printf("\n6-构建哈夫曼树并打印哈夫曼树");
printf("\n7-求各叶子节点的哈夫曼编码");
printf("\n8-进行编码译码");
printf("\n请输入你要执行的操作:
\n");
scanf("%d",&c);
switch(c)
{case0:
printf("输入叶子节点个数n:
");
scanf("%d",&n);
m=2\*n-1;
break;
case1:
getcode(ch,w,N);
n=N[0];
m=2\*n-1;
break;
case2:
printf("\n请输入%d个数据的权值:
\n",n);
for(i=1;i<=n;i++)
{printf("%d:
",i);
fflush(stdin);//清空输入缓存区
scanf("%d",&w[i]);
}
printf("请输入%d个数据的字符代表:
\n",n);
for(i=1;i<=n;i++)
{printf("%d号为:
",i);
fflush(stdin);//清空输入缓存区
scanf("%c",&ch[i]);
}
break;
case3:
printf("请输入要破解的电文二进制编码:
\n");
scanf("%s",code);
break;
case4:
save(n,w,code,ch);
break;
case5:
read(n,w,code,ch);
break;
case6:
createHuffmanTree(&HT,w,n,ch);
printf("\n\*\*\**\*\*\*\*\*\*打印哈夫曼树\*\*\**\*\*\*\*\n");
printf("序号\t字符\t权值\t双亲\t左孩子\t右孩子\n");
for(i=1;i<=m;i++)
printf("%d\t%c\t%d\t%d\t%d\t%d\t\n",i,HT[i].c,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild);
break;
case7:
creatHuffmanCode(HT,&HC,n);
break;
case8:
Translatecode(HT,&HC,n,code);
break;
default:
break;
}
if((c<0)||(c>8))
break;
}}
1
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
运行结果:
首先进行输入节点信息并存储文件的方法构建哈夫曼树
由于太长只截取部分编码。
完整编码为000000001010011111100000000101001111000011110110001111110101111000011001100110110111001111010000110110010011111011110110011111110010000100101110000010111101000110011
保存文件
关闭程序,重新开始,此时选择读取文件方式构建哈夫曼树,文件hafuman.txt中存储着各叶子节点权值,对应字符和待编译的二进制编码,读取文件截图如下,其他操作6.7.8及结果同上。
给出另一种构建哈夫曼树的方法
字母个数为相应字母对应的权值
其他操作同输入节点建树或者读取文件建树相似,在此不再给出。