(老代码修复)
C语言-哈夫曼编码解码程序
功能:
- 输入一串大写英文字符,统计字母频率并输出哈夫曼编码
- 输入一串哈夫曼编码,解码成大写英文字母输出并统计字母频率
- 先续遍历创建的哈夫编码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define maxsize 100 /* 编码函数中,被编译的字符串的最大长度 */
#define max 100 /* 最大字符的个数 */
typedef struct /* 定义一个HUFFNODE结点 */
{
char data; /* 数据域 */
int weight; /* 权值域 */
int parent; /* 双亲域 */
int left; /* 左孩子域 */
int right; /* 右孩子域 */
}huffnode;
typedef struct /* 定义一个HUFFCODE结点 */
{
char cd[max]; /* 数组cd存放哈夫曼编码 */
int start;
}huffcode;
huffnode *input() /* 输入函数,(与huffnode *init()函数有相同的功能) */
{
static huffnode ht[2*max];//VC++环境中要加关键字static
int i,n;
printf("enter elem num:");
scanf("%d",&n); /* 输入字符数 */
ht[0].weight=n;
//printf("n:%d",n);
for(i=1;i<=n;i++) {
getchar();
printf("%d elem data weight:",i);
scanf("%c%d",&ht[i].data,&ht[i].weight); /* 输入字符和权值 */
}
return ht;
}
huffnode *init() /* 初始化带权结点 */
{
static huffnode ht[2*max];
ht[0].weight=27; /* 字符个数 */
ht[1].data=' '; ht[1].weight=186;
ht[2].data='A'; ht[2].weight=64;
ht[3].data='B'; ht[3].weight=23;
ht[4].data='C'; ht[4].weight=22;
ht[5].data='D'; ht[5].weight=32;
ht[6].data='E'; ht[6].weight=103;
ht[7].data='F'; ht[7].weight=21;
ht[8].data='G'; ht[8].weight=15;
ht[9].data='H'; ht[9].weight=47;
ht[10].data='I'; ht[10].weight=57;
ht[11].data='J'; ht[11].weight=1;
ht[12].data='K'; ht[12].weight=5;
ht[13].data='L'; ht[13].weight=32;
ht[14].data='M'; ht[14].weight=20;
ht[15].data='N'; ht[15].weight=20;
ht[16].data='O'; ht[16].weight=56;
ht[17].data='P'; ht[17].weight=19;
ht[18].data='Q'; ht[18].weight=2;
ht[19].data='R'; ht[19].weight=50;
ht[20].data='S'; ht[20].weight=51;
ht[21].data='T'; ht[21].weight=55;
ht[22].data='U'; ht[22].weight=30;
ht[23].data='V'; ht[23].weight=10;
ht[24].data='W'; ht[24].weight=11;
ht[25].data='X'; ht[25].weight=2;
ht[26].data='Y'; ht[26].weight=21;
ht[27].data='Z'; ht[27].weight=2;
return ht;
}
void hfmtree(huffnode ht[]) /* 创建一棵哈夫曼树 */
{
/* m1为最小权值,x1为其在ht中的下标;m2为第二小权值,x2为其下标 */
int i,k,x1,x2,n,m1,m2;
n=ht[0].weight;
for(i=1;i<=2*n-1;i++)
ht[i].parent=ht[i].left=ht[i].right=0; /* 对ht的parent,left,right域进行初始化 */
for(i=n+1;i<=2*n-1;i++){
m1=m2=10000; /* m1,m2初值无限大 */
x1=x2=0; /* x1, x2初值为0 */
for(k=1;k<=i-1;k++) /* k为可以进行比较的结点的下标 */
if(ht[k].parent==0) /* 当前结点的父结点不存在时 */
if(ht[k].weight<m1) /* 当前结点的权值比最小权值还小时 */
{
m2=m1;
x2=x1;
m1=ht[k].weight;
x1=k;
}
else if(ht[k].weight<m2) /* 当前结点的权值比最小权值大但比第二最小权值小时 */
{
m2=ht[k].weight;
x2=k;
}
ht[x1].parent=i;
ht[x2].parent=i;
ht[i].weight=ht[x1].weight+ht[x2].weight;
ht[i].left=x1;
ht[i].right=x2;
}
}
huffcode *output(huffnode ht[]) /* 输出哈夫曼编码 */
{
static huffcode hcd[max];
huffcode d;
int i,n,c,f,k,x;
n=ht[0].weight;
for(i=1;i<=n;i++)
{
d.start=n+1; /* d.start为栈顶 */
c=i; /* c存放当前结点 */
f=ht[i].parent; /* f存放当前结点的父结点 */
while(f!=0)
{
if(ht[f].left==c) /* 若当前结点在其父结点的左边时 */
d.cd[--d.start]='0';
else
d.cd[--d.start]='1'; /* 当前结点在其父结点的右边时 */
c=f; /* 当前结点的父结点赋予c */
f=ht[f].parent; /* c的父结点赋予f */
}
hcd[i]=d; /* 将当前结点的哈夫曼编码赋予hcd[i]数组 */
}
printf("output huffman code:\n");
for(i=1;i<=n;i++) /* 输出各个结点的哈夫曼编码 */
{
if(i==1) printf("\n");
if(i==2) printf("\n");
printf("\t %c:",ht[i].data);
x=hcd[i].start;
for(k=x;k<=n;k++) /* 通过栈输出哈夫曼编码 */
printf("%c",hcd[i].cd[k]);
int m=(k-x)%12;
for(int l=0;l<12-m;l++)
printf(" ");
if(i%4==0)
printf("\n");
}
return hcd;
}
void coding(huffcode hcd[],huffnode ht[]) /* 编码函数,给定一个字符串,输出其对应的哈夫曼编码 */
{
int i,j,n,m,k,x;
char in[maxsize],out[2*maxsize]; /* in存放需编译的字符串,out存放编译后的代码 */
int count[26]={0};
m=0; /* m为out数组的下标 */
printf("enter a string:");
getchar();
gets(in);
n=strlen(in);
for(i=0;i<n;i++)
{
if(in[i]>='A'&&in[i]<'Z'){
count[in[i]-'A']++;//字符的次数count[in[i]-'A']增1
}
if(in[i]=='0') break;
}
for(i=0;i<n;i++)
{
for(j=1;j<=ht[0].weight;j++) /* ht[0].weight存放的是带权结点的个数 */
if(in[i]==ht[j].data) /* 若输入字符和一个带权结点相同 */
{
x=hcd[j].start;
for(k=x;k<=ht[0].weight;k++)
out[m++]=hcd[j].cd[k];
}
}
printf("the coding result is:\n");
for(i=0;i<m;i++)
printf("%c",out[i]);
printf("\n");
printf("出现的频率:\n");
for(i=0;i<26;i++)
{
if(count[i]==0) continue;
double p=count[i]*1.0*100/n;
printf("count of %c is %.2lf%%",'A'+i,p);
printf("\n");
}
}
void decoding(huffcode hcd[],huffnode ht[]) /* 译码函数,输入一个代码流,输出其对应的字符 */
{
int i,j,n,k,x,m,w;
char in[maxsize*2],out[maxsize]; /* in数组存放代码流,out数组存放对应字母 */
int count[26]={0};
printf("enter code stream:\n");
scanf("%s",in);
n=strlen(in);
i=0; m=0; /* i为in数组的下标,m为out数组的下标 */
while(i<n)
{
for(j=1;j<=ht[0].weight;j++) /* ht[0].weight为带权结点的个数 */
{
x=hcd[j].start;
for(k=x,w=i;k<=ht[0].weight;k++,w++) /* k为hcd[j].cd[]的下标 */
if(in[w]!=hcd[j].cd[k])
break;
if(k>ht[0].weight)
{
out[m++]=ht[j].data;
break;
}
}
i=w;
}
for(i=0;i<m;i++) /* 输出结果 */
printf("%c",out[i]);
printf("\n");
for(i=0;i<n;i++)
{
if(out[i]>='A'&&out[i]<'Z'){
count[out[i]-'A']++;//字符的次数count[in[i]-'A']增1
}
if(out[i]=='0') break;
}
printf("出现的频率:\n");
for(i=0;i<26;i++)
{
if(count[i]==0) continue;
double p=count[i]*1.0*100/m;
printf("count of %c is %.2lf%%",'A'+i,p);
printf("\n");
}
}
void disp(huffnode ht[]) /* 先续遍历创建的哈夫编码 */
{
int top,i,j,stack[maxsize];
top=0;
printf("the tree is:");
for(i=1;i<=ht[0].weight*2-1;i++) /* 寻找父结点为0的结点 */
if(ht[i].parent==0)
break;
do
{
if(i!=0) /* i为根结点 */
{
printf("%d ",ht[i].weight);
stack[top++]=i; /* 根结点进栈 */
while(ht[i].left!=0)
{
i=ht[i].left; /* 子树的根结点赋予i */
printf("%d ",ht[i].weight);
stack[top++]=i;
}
}
j=stack[--top];
i=ht[j].right;
}while(top>=0||i!=0);
printf("\n");
}
int main()
{
int select;
huffnode *ht;//其实是个为分配内存的动态数组
huffcode *hcd;
ht=init();
hfmtree(ht);
hcd=output(ht);
do
{
printf("\n");
printf("\n");
printf("\n");
printf("\t\t|--------------------------Menu-----------------------------|\n");
printf("\t\t|\t\t 0. exit |\n");
printf("\t\t|\t\t 1. 输入字母(大写),编码,统计频率 |\n");
printf("\t\t|\t\t 2. 输入编码,解码成大写字母输出 |\n");
printf("\t\t|\t\t 3. disp |\n");
printf("\t\t|-----------------------------------------------------------|\n\n");
printf("enter(0---3): ");
scanf("%d",&select);
if(select==0)
{
printf("THANK YOU USE THE SOFTWARE!\n");
exit(1);
}
if(select==1) coding(hcd,ht);
else if(select==2) decoding(hcd,ht);
else disp(ht);
}while(1);
return 0;
}
TEST
主界面
解码
编码