/**************************************************************************
* HuffmanEncoding.cpp:
***************************************************************************
*本程序用C语言实现哈弗曼编码。
*读取指定文件,将文件中的字符用哈弗曼编码,将编码结果输出到指定文件中,并且
*同时将编码结果打印到屏幕上。
**************************************************************************/
#include <stdio.h>
#include <string.h>
#define MAX 256 //8位ASCII码 共256个。
#define INF 0xffffff //设定某个字符允许出现的最大次数。
typedef struct
{
char c; //字符c
int cnt; //字符c出现的次数
}Character;
typedef struct //霍夫曼树的节点
{
int weight;
int parent;
int ld;
int rd;
}Huffmantree;
typedef struct
{
char ch;
int num;
}myNode;
typedef struct //字符和其对应的编码
{
char ch; //字符
int s[50]; //ch的编码
int len; //编码长度
}mycode;
int nNode; //叶子节点数目
int totalNode; //霍夫曼树的总节点个数
char toCode[100000] ; //待编码的字符串
myNode myToCode[100000]; //待编码的字符串和权值
int weightOfToCode[100000] ; //字符串的权值!
Huffmantree myHuffmantree[1000000]; //霍夫曼树(数组模拟)
char allchar[1000000]; //所有出现过的字符
mycode coder[1000000]; //字符与对应的编码
int Coding[100000]; //译码之后的01串
int lenOfCoding ; //01串的长度
Character chara[MAX]; //存放每个待编码字符出现的次数
int chara_cnt=0; //待编码的字符的个数
void read(); //读取文件中待编码的字符
void build(int n); //建立霍夫曼树
void select(int *a,int *b); //选择两个权值最小的节点
void Code(); //编码
void printCode(); //打印编码
int main()
{
read(); //读取待编码文件中的字符,并打印出现的所有字符及其出现的次数。
nNode=chara_cnt; //子节点个数
totalNode=nNode; //totalNode初始值为nNode
build(nNode); //建立节点
Code(); //编码
printCode(); //打印编码结果
system("pause");
return 0;
}
void read()
{
char filename[100];
FILE* fp=0;
int i=0;
int j=0;
int len=0;
char *p=toCode;
int h=1;
printf("请输入待编码文件的名字(e.g. F:\\test\\in_file.txt):\n");
scanf("%s",filename);
if((fp=fopen(filename,"r"))==NULL)
{
printf("cannot open this file\n");
exit(1);
}
while(!feof(fp))
{
char c=fgetc(fp);
if(c!=EOF&&c!='\n') // 防止读入EOF标志,回车符号进行编码
*p++=c;
}
len=strlen(toCode);
for(i=0;i<len;i++) //统计字符串中各字符出现的频次!
{
for( j=0;j<chara_cnt;++j){
if(toCode[i]==chara[j].c)
break;
}
if(j==chara_cnt) //当前字母没有出现过
{
chara[chara_cnt].c=toCode[i];
(chara[chara_cnt].cnt)++;
chara_cnt++;
}
else
{
(chara[j].cnt)++;
}
}
for(j=0;j<chara_cnt;++j)
{
myToCode[h].ch=chara[j].c;
allchar[h]=chara[j].c;
weightOfToCode[h]=chara[j].cnt;
myToCode[h++].num=chara[j].cnt;
}
printf("--------------------------字符统计如下----------------------------------\n");
printf(" 字符 次数\n");
for(i=1;i<=chara_cnt;i++) //显示将要编码的字符串中的各字符和他出现的次数!
{
printf(" %c %d\n",myToCode[i].ch,myToCode[i].num);
}
}
void build(int n) //建立霍夫曼树
{
int i;
int m=2*n-1; //n个叶子节点的霍夫曼树总节点数为2*n-1
for(i=1;i<=n;i++) //初始化霍夫曼数组
{
myHuffmantree[i].weight=weightOfToCode[i]; //叶子节点权值为字符出现次数
myHuffmantree[i].ld=-1; //叶子节点没有左孩子
myHuffmantree[i].rd=-1; //叶子节点没有右孩子
myHuffmantree[i].parent=i; //叶子节点父节点先初始化为他本身
}
for(i=n+1;i<=m;i++)
{
int a,b;
select(&a,&b);
myHuffmantree[a].parent=i;
myHuffmantree[b].parent=i;
myHuffmantree[i].ld=a;
myHuffmantree[i].rd=b;
myHuffmantree[i].weight=myHuffmantree[a].weight+myHuffmantree[b].weight;
myHuffmantree[i].parent=i;
}
}
void Code() //编码
{
int i,j;
int Len; //待编码的字符的总长度
int numOfCode[100000];
Len=strlen(toCode); //待编码的字符的总长度
printf("--------------------------各字符编码结果如下----------------------------\n");
if(Len==1)
{
printf("%c : 0\n",toCode[0]);
return ;
}
for(i=1;i<=nNode;i++)
{
int h=0;
int x=0;
int k;
j=i;
while(myHuffmantree[j].parent!=j)
{
int x=j;
j=myHuffmantree[j].parent;
if(myHuffmantree[j].ld==x)
{
numOfCode[h++]=0;
}
else if(myHuffmantree[j].rd==x)
{
numOfCode[h++]=1;
}
}
printf(" %c : ",allchar[i]);
coder[i].len=h;
coder[i].ch=allchar[i];
for( k=h-1;k>=0;k--)
{
coder[i].s[x++]=numOfCode[k];
printf("%d",numOfCode[k]);
}
printf("\n");
}
}
void select(int *a,int *b) //选择两个权值最小的节点
{
int i;
int min1=INF;
int min2=INF;
int sign1=1; //最小值的下标
int sign2=2; //次小值的下标
for(i=1;i<=totalNode;i++)
{
if(myHuffmantree[i].parent==i) //说明其是已经更新过的节点
{
if(myHuffmantree[i].weight<min1) 找出权重最小的节点sign1
{
min1=myHuffmantree[i].weight;
sign1=i;
}
}
}
for(i=1;i<=totalNode;i++)
{
if(myHuffmantree[i].parent==i) //说明其是已经更新过的节点
{
if(myHuffmantree[i].weight<min2&&i!=sign1) //找出除sign1外的权重最小的sign2
{
min2=myHuffmantree[i].weight;
sign2=i;
}
}
}
*a=sign1;
*b=sign2;
totalNode++; //总节点数加1
}
void printCode() //打印编码结果!
{
int i,j,k;
int Len=strlen(toCode);
int h=0;
FILE *fp=0;
if((fp=fopen("./Encode.txt","w"))==NULL)
{
printf("cannot open this file\n");
exit(1);
}
printf("\n------------------------字符串的编码结果如下------------------------\n");
if(Len==1) //长度为1的时候特殊考虑
{
printf("0\n");
fprintf(fp,"0\n");
return ;
}
for(i=0;i<Len;i++)
{
for( j=1;j<=nNode;j++)
{
if(toCode[i]==coder[j].ch)
{
for( k=0;k<coder[j].len;k++)
{
printf("%d",coder[j].s[k]); //打印文件到屏幕
fprintf(fp,"%d",coder[j].s[k]);//保存编码到文件
Coding[h++]=coder[j].s[k]; //保存编码到数组
}
}
}
}
lenOfCoding=h;
printf("\n编码码流总长度为:%d\n",lenOfCoding);
fprintf(fp,"\n");
printf("\n为方便查阅,编码结果另存到当前工程目录下的Encode.txt文件中!!!\n");
}
转载于:https://blog.51cto.com/lihaichuan/1187071