/**********************************************************************
题 目:
Huffman编码与解码(必做)(Huffman编码、二叉树)
[问题描述]
对一篇不少于5000字符的英文文章(source.txt),统计各字符出现的次数,
实现Huffman编码(code.dat),以及对编码结果的解码(recode.txt)。
[基本要求]
(1) 输出每个字符出现的次数和编码,并存储文件(Huffman.txt)。
(2) 在Huffman编码后,英文文章编码结果保存到文件中(code.dat),
编码结果必须是二进制形式,即0 1的信息用比特位表示,不能用字符'0'和'1'表示(*)。
(3) 实现解码功能。
开始日期:2019年12月20日
最后修改日期:2019年12月20日
***********************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//——————————————————HuffmanTree中H->w[0]不存数据————————————————//
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 500
#define LineNumber 500//每行最多读取的数目
typedef int Status;
typedef struct WordCount
{
char word;//存放字符
int freq;//存放字符出现次数
int parent,lchild,rchild;//存储双亲 左孩子 右孩子
int place;//用来保存第一次堆排序后,建立哈夫曼表前的相对位置
char *HuffmanCode;//存储哈夫曼编码
}WordCount,*WC;
typedef struct
{
WC w;
int number;//总字符数
}HuffmanTree,*HTree;
Status InitHTree(HTree &H)
//————————————————————————————————————————初始化哈夫曼树
{
H=(HTree)malloc(sizeof(HuffmanTree));
H->w=(WC)malloc(MAXSIZE*sizeof(WordCount));
H->number=0;
return OK;
}
Status CountTheWord(HTree &H,FILE *fp)
//————————————————————————————————————————统计文章各字符个数并写入哈夫曼树
{
int TempCount;//判断字符是否重复时的临时变量
char wd;//临时存储单个字符
char LineCount[LineNumber];//控制一行最多可以读取的字符数
int i,j,WordCountNum=1;//不重复的字符个数
while(!feof(fp))
{
memset(LineCount,0,LineNumber);//将WordCount清空
fgets(LineCount,LineNumber,fp);//读一行字符存在WordCount中
for(i=0;i<strlen(LineCount);i++)
{
wd=LineCount[i];
if(WordCountNum==1)
//如果是第一个字符则不需要考虑重复与否 可直接写入Huffman树
{
H->w[WordCountNum].word=wd;
H->w[WordCountNum].freq=1;//频率加一
H->number=1;//树中字符数加一
H->w[WordCountNum].place=WordCountNum;
WordCountNum++;
}
else//若不是文件中第一个字符
{
TempCount=WordCountNum-1;//检测 除此字符外的其他字符是否与该字符重复
while(TempCount>=1)
{
if(H->w[TempCount].word==wd)
{
H->w[TempCount].freq++;
break;
}
--TempCount;
if(TempCount<1)
{
H->number++;
H->w[WordCountNum].freq=1;
H->w[WordCountNum].word=wd;
H->w[WordCountNum].place=WordCountNum;
++WordCountNum;
}
}
}
}
}
return OK;
}
Status SelectSort(HTree &H)//选择排序
{
int i,j,temp,min;
WordCount w;
for(i=1;i<=H->number;i++)
{
min=i;
for(j=i+1;j<=H->number;j++)
{
if(H->w[j].freq<H->w[min].freq)
min=j;
}
if(min!=i)
{
w=H->w[i];
H->w[i]=H->w[min];
H->w[min]=w;
}
}
for(int i=1;i<=H->number;i++)
{
H->w[i].place=i;
}
return OK;
}
Status ShowHTree(HTree H)
{
printf("\n位置 字符\t权值\t双亲\t左孩子 右孩子\t\n");
for(int i=1;i<=H->number;i++)
{
printf("NO.%d\t%c\t%d\t%d\t%d\t%d\t\n",
H->w[i].place,H->w[i].word,H->w[i].freq,H->w[i].parent,H->w[i].lchild,H->w[i].rchild);
}
return OK;
}
Status Select(HTree H,int i,int &s1,int &s2)
//————————————————————————在H[1..i]中选择parent为0且权值(此处为频率)最小的两个结点,其序号分别为s1和s2
{
for(int j=1;j<=i;j++)
{
if(H->w[j].parent==0)
{
s1=j;
++j;
while(j<=i)
{
if(H->w[j].parent==0)
{
s2=j;
break;
}
++j;
}
break;
}
}
return OK;
}
Status HuffmanCoding(HTree &HT,HTree HC)
//——————————————————————————————————————实现赫夫曼编码
//————————————————————————————————————————HC为没有编码的树
{
if(HC->number<=1)
return ERROR;
int m=2*(HC->number)-1;//含有n个叶结点的树有2*n-1个结点
HT=(HTree)malloc(sizeof(HuffmanTree));
HT->w=(WC)malloc(sizeof(WordCount)*(m+1));//0号空间未用
HT->number=0;
int i;
//初始化HT
//赋予权值 没爹没娘没孩子
//for(i=1;i<=HC->number;i++)
SelectSort(HC);
for(i=1;i<=m;i++)
{
if(i<=HC->number)
{
HT->w[i].freq=HC->w[i].freq;
HT->w[i].word=HC->w[i].word;
}
else
{
HT->w[i].freq=0;
HT->w[i].word=' ';
}
HT->w[i].HuffmanCode="";
HT->w[i].parent=0;
HT->w[i].lchild=0;
HT->w[i].rchild=0;
HT->w[i].place=i;
HT->number++;
}
//SelectSort(HT);
//下面开始建立赫夫曼树
int s1,s2;
for(i=HC->number+1;i<=m;i++)
{
Select(HT,i-1,s1,s2);
//在H->w[1..i-1]中选择parent为0且权值(此处为频率)最小的两个结点,其序号分别为s1和s2
HT->w[s1].parent=i;
HT->w[s2].parent=i;
HT->w[i].lchild=s1;
HT->w[i].rchild=s2;
HT->w[i].word='#';//#代表双亲
HT->w[i].freq=(HT->w[s1].freq)+(HT->w[s2].freq);
}
//下面开始从叶子结点到根结点逆向求求赫夫曼编码
char *cd;
int n=HC->number;
int c,f,start=n-1;//标记开始位置
cd=(char*)malloc(sizeof(char)*(HC->number));//临时存储赫夫曼编码 编码成功后拷贝到H->w.huffmancode中
cd[HC->number-1]='\0';//末尾加结束符
for(i=1;i<=n;i++)//逐个字符求赫夫曼编码
{
start=n-1;
memset(cd,0,n*sizeof(char));
for(c=i,f=HT->w[c].parent;f!=0;c=f,f=HT->w[f].parent)
{
if(HT->w[f].lchild==c)
cd[--start]='0';
else
cd[--start]='1';
}
HT->w[i].HuffmanCode=(char*)malloc(sizeof(char)*(n-start));
strcpy(HT->w[i].HuffmanCode,&cd[start]);//从cd复制编码串到w.HuffmanCode
HT->w[i].HuffmanCode[n-start]='\0';//字符串末尾加结束符
}
free(cd);//释放空间
return OK;
}
Status ShowFreq(HTree H)
//————————————————————————————————————————————————打印字符出现频率
{
printf("位置 字符 权值\n");
for(int i=1;i<=H->number;i++)
{
printf("No.%d\t%c\t%d\n",H->w[i].place,H->w[i].word,H->w[i].freq);
}
return OK;
}
Status ShowHuffmanCode(HTree H)
//——————————————————————————————————————————————————打印赫夫曼编码串
{
printf("位置 字符 编码串\n");
for(int i=1;i<=(H->number+1)/2;i++)
{
printf("No.%d\t%c\t%s\n",H->w[i].place,H->w[i].word,H->w[i].HuffmanCode);
}
return OK;
}
Status WriteInHuffmanTxt(HTree HT)
//————————————————————————————————————将每个字符出现的次数和编码,并存储到文件(Huffman.txt)
{
FILE *fp;
if((fp=fopen("Huffman.txt","w"))==NULL)
{
printf("\n打开文件Huffman.txt失败...\n");
exit(0);
}
for(int i=1;i<=(HT->number+1)/2;i++)
{
fprintf(fp,"%c %d %s\n",HT->w[i].word,HT->w[i].freq,HT->w[i].HuffmanCode);
}
fclose(fp);
return OK;
}
typedef struct
{
unsigned int a:1;
}bite;//设置位段,存储一个bite
Status WriteInCodeDat(HTree HT)
//————————————————————————————————在Huffman编码后,英文文章编码结果保存到文件中(code.dat),
//——————————————————————编码结果必须是二进制形式,即0 1的信息用比特位表示,不能用字符'0'和'1'表示(*)。
{
FILE *fp,*wp,*p;
if((fp=fopen("source.txt","r"))==NULL)//读出原文
{
printf("\n打开文件code.dat失败...\n");
exit(0);
}
if((wp=fopen("code.dat","wb"))==NULL)//以只写的形式打开二进制文件code.dat wb:建立新文件
{
printf("\n打开文件code.dat失败...\n");
exit(0);
}
if((p=fopen("word.txt","w"))==NULL)//读出原文
{
printf("\n打开文件word.txt失败...\n");
exit(0);
}
int i;
bite Save;
char elem;
for(i=1;i<(HT->number+1)/2;i++)
{
fprintf(p,"%c",HT->w[i].word);
}
fclose(p);
while(!feof(fp))
{
fscanf(fp,"%c",&elem);//读取一个字符
for(i=1;i<=(HT->number+1)/2;i++)
{
if(elem==HT->w[i].word)//找到该字符对应编码
{
for(int j=0;j<strlen(HT->w[i].HuffmanCode);j++)//逐个字符将赫夫曼编码串写入文件
{
if(HT->w[i].HuffmanCode[j]=='1')
Save.a=1;
else
Save.a=0;
fwrite(&Save,sizeof(bite),1,wp);//用比特位写进文件
}
break;//已找到 跳出循环进行下一次寻找
}
}
}
fclose(fp);
fclose(wp);
return OK;
}
Status DeCode(HTree H)
//————————————————————————————————————————————————编码结果的解码(写入recode.txt)
{
FILE *fp,*wp;
//InitHTree(H);//重建一颗赫夫曼树
if((fp=fopen("code.dat","rb"))==NULL)
//以只读方式打开按位域存储的code.dat
{
printf("\n解码过程中打开文件code.dat失败...\n");
exit(0);
}
if((wp=fopen("recode.txt","w"))==NULL)
{
printf("\n解码过程中打开recode.txt失败...\n");
exit(0);
}
int i=0,j=1;
int n=(H->number+1)/2;
char count[n];
memset(count,0,n*(sizeof(char)));
bite Put;//输出位域存储的数据
int number=strlen(H->w[n].HuffmanCode);//存储最短的赫夫曼编码 减少比较次数
while(!feof(fp))//读取二进制文件 二进制文件中依次存储源文件中各个字符的比特位形式赫夫曼编码
{
if(i<number)
{
fread(&Put,sizeof(bite),1,fp);
if(Put.a==1)
count[i]='1';
else
count[i]='0';
i++;
}
else
{
while(j <= H->number)//遍历H中每一个字符
{
if(strcmp(count,H->w[j].HuffmanCode)==0)//该编码是赫夫曼编码 写入recode
{
fprintf(wp,"%c",H->w[j].word);
memset(count,0,n*(sizeof(char)));
i=0;
j=1;
break;//这个字符成功译出 进行下一次查找
}
j++;
if(j>H->number)//由于赫夫曼编码的唯一性 可知count中存储的还不是赫夫曼编码 继续进行读入
{
fread(&Put,sizeof(bite),1,fp);
if(Put.a==1)
count[i]='1';
else
count[i]='0';
i++;
j=1;//重新在H所存所有字符中查找
}
}
}
}
fclose(fp);
fclose(wp);
return OK;
}
Status ShowTheDecodeRecode()
//—————————————————————————————————————————————————显示解码结果
{
FILE *fp;
if((fp=fopen("recode.txt","r"))==NULL)
{
printf("\n打印解码后的文章是recode.txt打开失败...\n");
exit(0);
}
char buffer[LineNumber];
while(!feof(fp))
{
fgets(buffer,LineNumber,fp);
printf("%s",buffer);
}
fclose(fp);
return OK;
}
int main()
{
HTree HC;
HTree HT;
InitHTree(HC);
//打开文件读取source.txt
FILE *fp;
if((fp=(fopen("source.txt","r")))==NULL)
{
printf("\n打开文件source.txt失败...\n");
exit(0);
}
CountTheWord(HC,fp);
fclose(fp);
printf("\n按任意键显示source.txt内各字符出现的频率......\n");
system("pause");
printf("——————————————————————————\n");
printf("该文章各字符出现的频率为:\n");
ShowFreq(HC);
// printf("选择排序后该文章各字符出现的频率为:\n");
// SelectSort(HC);
// ShowFreq(HC);
HuffmanCoding(HT,HC);
printf("\n——————————————————————————\n\n");
printf("\n按任意键显示赫夫曼编码表.......\n");
system("pause");
printf("————————————————————————————\n");
printf("赫夫曼编码表如下:\n");
ShowHTree(HT);
printf("\n——————————————————————————\n\n");
printf("\n按任意键显示各字符赫夫曼编码串.......\n");
system("pause");
printf("————————————————————————————\n");
ShowHuffmanCode(HT);
printf("各字符赫夫曼编码串如上\n");
printf("\n——————————————————————————\n\n");
WriteInHuffmanTxt(HT);
printf("\n按任意键各字符频率及赫夫曼编码串写入文件Huffman.txt.......\n");
system("pause");
printf("————————————————————————————\n");
printf("成功将内容写入Huffman.txt\n");
printf("————————————————————————————\n");
printf("\n按任意键各字符赫夫曼编码串写入文件code.dat.......\n");
system("pause");
printf("————————————————————————————\n");
WriteInCodeDat(HT);
printf("成功将内容写入code.dat\n");
printf("————————————————————————————\n");
printf("\n按任意键进行解码并将结果写入文件recode.txt.......\n");
system("pause");
printf("————————————————————————————\n");
DeCode(HT);
printf("成功将解码并将内容写入recode.txt\n");
printf("————————————————————————————\n");
printf("\n按任意键显示原文......\n");
system("pause");
printf("————————————————————————————\n");
printf("原文如下:\n");
ShowTheDecodeRecode();
return 0;
}
数据结构 K2.赫夫曼编码与解码
最新推荐文章于 2022-02-28 09:55:58 发布