① 问题描述:给定n个字符的权值数组w,根据哈夫曼编码与译码规则,实现一个哈夫曼编/译码系统(利用实验指导书上的27个字符的数据进行实验)。
② 利用顺序表存储Huffman树,编码结果的存储方式采用书上的结构。
③ Huffman树的构造约定如下:
根的权值较小的子树作为左子树,当权值相等时,则先生成的子树是左子树;
按照结点的生成次序选择权值较小的两棵子树构造Huffman树;
从叶子结点到根结点逆向求出每个字符的Huffman编码,不采用递归方法;
从根结点开始实现译码,要求被译码的字符数大于20个字符。
④ 采用文件方式存储n个权值和待翻译的二进制代码,其余数据均不采用文件存储。
序号 字符 权值 双亲结点 左孩子 右孩子
1 □ 186 0 0 0
2 A 64 0 0 0
3 B 13 0 0 0
4 C 22 0 0 0
5 D 32 0 0 0
6 E 103 0 0 0
7 F 21 0 0 0
8 G 15 0 0 0
9 H 47 0 0 0
10 I 57 0 0 0
11 J 1 0 0 0
12 K 5 0 0 0
13 L 32 0 0 0
14 M 20 0 0 0
15 N 57 0 0 0
16 O 63 0 0 0
17 P 15 0 0 0
18 Q 1 0 0 0
19 R 48 0 0 0
20 S 51 0 0 0
21 T 80 0 0 0
22 U 23 0 0 0
23 V 8 0 0 0
24 W 18 0 0 0
25 X 1 0 0 0
26 Y 16 0 0 0
27 Z 1 0 0 0
void save(int n,int w[],char code[],char ch[])
{
FILE *fp;
char filename[20];
int i;
printf(“请输入要保存的文件名称:\n”);
scanf("%s",filename);
if((fp=fopen(filename,“wb”))= =NULL)//打开输出文件
{
printf(“cannot open file:\n”);
return;
}
for(i=0;i<=n;i++)
{
if(fwrite(&w[i],sizeof(int),1,fp)!=1)//数组向磁盘文件写入各叶子节点权值
printf(“file write error!\n”);
}
for(i=0;i<=n;i++)
{
if(fwrite(&ch[i],sizeof(char),1,fp)!=1)//数组向磁盘文件写入各叶子字母代表
printf(“file write error!\n”);
}
for(i=0;i<=strlen(code);i++)
{
if(fwrite(&code[i],sizeof(char),1,fp)!=1)//数组向磁盘文件写入待破解电码代码
printf(“file write error!\n”);
}
printf(“保存文件成功!!!”);
fclose(fp);
}
void read(int n,int w[],char code[],char ch[])
{
FILE fp;
char filename[20];
int i;
printf(“请输入读入的文件名:\n”);
scanf("%s",filename);
if((fp=fopen(filename,“rb”))= =NULL)//以只读方式打开二进制文件
{ printf(“can not open file\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);
}
void createHuffmanTree(HuffmanTree *HT, int w[], int n,char ch[])
{
int m = 2 * n - 1;
int s1,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");
}
void select(HuffmanTree *HT, int n, int *s1, int *s2)
{ int i = 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;
}
}
//从叶子到根逆向求每个字符的哈夫曼编码
void creatHuffmanCode(HuffmanTree HT,HuffmanCode HC,int n)
{
int i,j,c;
int start;
int f;
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); }
}
//定义编码解码函数
void Translatecode(HuffmanTree HT,HuffmanCode HC,int n,char code[])
{ int i,j,m,c,choice;
int start;
int f;
char zcode[100];//存放输入的字母字符串
m=2n-1;
HC=(HuffmanCode *)malloc((n+1)*sizeof(char ));//分配n个字符编码的头指针向量
char cd=(char )malloc(nsizeof(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]);
}
else if(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;
else m=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");
else printf("\n电文输入错误!\n"); }
else if(choice==2)
break;}
}
//定义获取叶子节点权值和字母的函数
void getcode(char ch[],int w[],int N[]){
int i,j,count;
int k=1;
char temp;
char zcode[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;}
void main()
{ HuffmanTree HT;
HuffmanCode HC;//各叶子节点的哈夫曼编码
char ch[30];//各叶子节点的字符代表
char code[400]; //存放输入的二进制编码字符串
int w[30];//w数组存储权值
int i,j,n,m,c;
int N[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©
{ case 0:
printf(“输入叶子节点个数 n: " );
scanf(”%d",&n);
m=2*n-1;
break;
case 1:
getcode(ch,w,N);
n=N[0];
m=2*n-1;
break;
case 2:
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;
case 3:
printf(“请输入要破解的电文二进制编码:\n”);
scanf("%s",code);
break;
case 4:
save(n,w,code,ch);
break;
case 5:
read(n,w,code,ch);
break;
case 6:
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;
case 7:
creatHuffmanCode(HT,&HC,n);
break;
case 8:
Translatecode(HT,&HC,n,code);
break;
default: break;
}
if((c<0)||(c>8))
break;
} }
运行结果:首先进行输入节点信息并存储文件的方法构建哈夫曼树
由于太长只截取部分编码。完整编码为000000001010011111100000000101001111000011110110001111110101111000011001100110110111001111010000110110010011111011110110011111110010000100101110000010111101000110011
关闭程序,重新开始,此时选择读取文件方式构建哈夫曼树,文件hafuman.txt中存储着各叶子节点权值,对应字符和待编译的二进制编码,读取文件截图如下,其他操作6.7.8及结果同上。
给出另一种构建哈夫曼树的方法
其他操作同输入节点建树或者读取文件建树相似,在此不再给出。