目录
一、实验目的
熟悉非线性结构的特点 , 掌握非线性结构的存储方式及各种操作的实现方法,同时对自顶向下的程序设计方法、应用程序界面的设计、非线性结构的文件存储方法等方面的辑程技术进行训练。
二、问题描述
利用哈夫曼编码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统,试为这样的信息收发站写一个哈夫曼编译码系统。
三、需求分析
一个完整的系统应具有以下功能:
(1) I: 初始化。从终端读字符集大小 n ,及 n 个字符和 n 个权值,建立哈夫曼树,并将其存于文件hfmtree中。
(2) C: 编码。利用已建好的哈夫曼树(如不在内存,则从文件hfmtree中读入),对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中。
(3) D: 译码。利用已建好的哈夫曼树将文件codefile中的代码进行译码,结果存入文件textfile中。
(4) P: 打印代码文件。将文件codefi1e以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件codeprint中。
(5) T:打印哈夫曼树。将已在内存中的哈夫曼树以直观的方式显示在屏幕上,同时将此字符形式的哈夫曼树写入文件treeprint中。
(6) E: 表示结束运性行结束,用户键入一个选择功能字符,则执行相应的功能,此功能执行完毕后再显示此菜单,直至用户选择了“E”为止。
四、方法描述
typedef struct
{
char alphabet;
int weight;
int parent,lch,rch;
}HTNode,*HuffmanTree;
为哈夫曼树结构体
初始化时,输入哈夫曼树叶子节点所代表的字符和字符频度。遍历哈夫曼树,利用select函数找到权值最小的两个值,作为叶子节点,后再寻找其双亲结点,直至根节点。还要根据哈夫曼树形成哈夫曼编码表,从哈夫曼树的第一行开始遍历,找到该叶子节点对应的双亲结点直到根节点,根据左右孩子确定编码的0,1存储到cd数组中,最终形成该字符对应的哈夫曼编码存储到HC二维数组中。
五、代码实现
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int n;
typedef struct
{
char alphabet;
int weight;
int parent,lch,rch;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;
char enterChoice();
void makeTree(HuffmanTree &HT,HuffmanCode &HC);//建立哈夫曼树
void Select(HuffmanTree &HT,int i,int* s1,int* s2);
void coding(HuffmanTree &HT,HuffmanCode &HC);//编码
void decode(HuffmanTree &HT,HuffmanCode &HC);//译码
void printCode();//打印代码文件
void printTree(HuffmanTree &HT,HuffmanCode &HC);//打印哈夫曼树
int main()
{
char choice;
HuffmanTree HT;
HuffmanCode HC;
while((choice=enterChoice())!='E')
{
switch(choice)
{
case 'I':
//初始化
makeTree(HT,HC);
break;
case 'C':
//编码
coding(HT,HC);
break;
case 'D':
//译码
decode(HT,HC);
break;
case 'P':
//打印代码文件
printCode();
break;
case 'T':
//打印哈夫曼树
printTree(HT,HC);
break;
}
getchar();
}
return 0;
}
/*用户菜单选择*/
char enterChoice()
{
char menuChoice;//存储用户菜单的选择
/*显示可选的菜单*/
printf("请输入您的选择\n");
printf("I-初始化\n");
printf("C-编码\n");
printf("D-译码\n");
printf("P-打印代码文件\n");
printf("T-打印哈夫曼树\n");
printf("E-退出系统\n");
menuChoice=getchar();//接受用户选择
return menuChoice;
}
/*构建哈夫曼树*/
void makeTree(HuffmanTree &HT,HuffmanCode &HC)
{
char *cd,al;
int m,i,s1,s2;
printf("请输入n的值\n");
scanf("%d",&n);//点数
if(n<=1) return;
/*初始化哈夫曼树*/
m=2*n-1;//边数=点数*2-1
HT=new HTNode[m+1];
for(i=1;i<=m;++i)
{
HT[i].lch=0;
HT[i].rch=0;
HT[i].parent=0;
}
for(i=1;i<=n;i++)
{
getchar();
printf("请输入字符\n");
al=getchar();
HT[i].alphabet=al;
printf("请输入字符频度\n");
scanf("%d",&HT[i].weight);
}
for(i=n+1;i<=m;i++)
{
Select(HT,i-1,&s1,&s2);
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lch=s1;
HT[i].rch=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
printf("哈夫曼树构建成功!\n");
/*生成哈夫曼编码*/
int start,c,f;
HC=(HuffmanCode)malloc((n+1)*sizeof(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].lch==c)
{
cd[--start]='0';
}
else
{
cd[--start]='1';
}
}
HC[i]=(char*)malloc((n-start)*sizeof(char));
strcpy(HC[i],&cd[start]);
}
printf("哈夫曼编码表建立成功!\n");
free(cd);
}
/*找出权值最小的两个结点*/
void Select(HuffmanTree &HT,int i,int* s1,int* s2)
{
int j,S1=0,S2=0;
for(j=1;j<=i;j++)
{
if(HT[j].parent!=0)
{
continue;
}
if(S1==0&&S2==0)
{
S1=HT[j].weight;
S2=HT[j].weight;
*s1=j;
*s2=j;
}
else if(S1==S2)
{
if(HT[j].weight<S1)
{
S1=HT[j].weight;
*s1=j;
}
else
{
S2=HT[j].weight;
*s2=j;
}
}
else
{
if(HT[j].weight<S1)
{
S2=S1;
*s2=*s1;
S1=HT[j].weight;
*s1=j;
}
else if(HT[j].weight<S2)
{
S2=HT[j].weight;
*s2=j;
}
}
}
}
/*编码*/
void coding(HuffmanTree &HT,HuffmanCode &HC)
{
int i,l;
FILE *fpTobe;//正文文件
FILE *fpCode;//编码文件
char test[30];//测试数据
char data;
/*输入测试数据文件*/
if((fpTobe=fopen("tobrtrans.dat","ab+"))==NULL)//打开编码文件
{
printf("文件有误\n");
exit(0);
}
printf("输入测试数据文件");
scanf("%s",test);
fseek(fpTobe,0,SEEK_END);
fwrite(&test,(29*sizeof(char)),1,fpTobe);
printf("测试数据录入完成!\n");
fclose(fpTobe);
/*测试输入完成*/
if((fpTobe=fopen("tobrtrans.dat","rb"))==NULL)//打开正文文件
{
printf("文件为空\n");
}
if((fpCode=fopen("codefile.dat","ab+"))==NULL)//打开编码文件
{
printf("文件有误\n");
exit(0);
}
while((fread(&data,sizeof(char),1,fpTobe))!=(int)NULL)
{
for(i=1;i<=n;i++)
{
if(HT[i].alphabet==data)
{
l=strlen(HC[i]);
fseek(fpCode,0,SEEK_END);
fwrite(HC[i],(l*sizeof(char)),1,fpCode);
break;
}
}
}
printf("正文文件编码成功!\n");
fclose(fpTobe);
fclose(fpCode);
}
/*译码*/
void decode(HuffmanTree &HT,HuffmanCode &HC)
{
int i,m,res=0;
FILE *fpDecode;//正文文件
FILE *fpCode;//编码文件
char data,DATA[n];
if((fpDecode=fopen("textfile.dat","ab+"))==NULL)//打开正文文件
{
printf("文件有误\n");
exit(0);
}
if((fpCode=fopen("codefile.dat","rb"))==NULL)//打开编码文件
{
printf("文件为空\n");
}
strcpy(DATA,"\0");
while((fread(&data,sizeof(char),1,fpCode))!=(int)NULL)
{
strncat(DATA,&data,1);
for(i=1;i<=n;i++)
{
m=strcmp(HC[i],DATA);
if(m==0)
{
res=1;
break;
}
}
if(res==1)
{
fseek(fpDecode,0,SEEK_END);
fwrite(&HT[i].alphabet,sizeof(char),1,fpDecode);
strcpy(DATA,"\0");
res=0;
}
}
printf("编码文件译码成功!\n");
fclose(fpDecode);
fclose(fpCode);
}
/*打印代码文件*/
void printCode()
{
int i=0;
FILE *fpCode;
char data;
if((fpCode=fopen("codefile.dat","rb"))==NULL)//打开编码文件
{
printf("文件为空\n");
}
else
{
while((fread(&data,sizeof(char),1,fpCode))!=(int)NULL)
{
printf("%c",data);
i++;
if(i%50==0)
{
printf("\n");
}
}
}
fclose(fpCode);
printf("\n");
printf("编码文件打印成功!\n");
}
/*打印哈夫曼树*/
void printTree(HuffmanTree &HT,HuffmanCode &HC)
{
int i;
printf("哈夫曼树\n");
printf("%-5s%-5s%-5s%-6s%-6s\n","字符","权值","双亲","左孩子","右孩子");
for(i=1;i<=(2*n-1);i++)
{
printf("%-5c%-5d%-5d%-6d%-6d\n",HT[i].alphabet,HT[i].weight,HT[i].parent,HT[i].lch,HT[i].rch);
}
printf("哈夫曼树打印成功!\n");
}
六、实验结果测试
主菜单选择:
进行初始化:
编码:
译码:
打印代码文件
打印哈夫曼树