#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef struct
{
char letter, *code;
int weight;
int parent, lchild, rchild;
} HTNode, *HuffmanTree;
//char a[28]=" ABCDEFGHIJKLMNOPQRSTUVWXYZ";//存字符
char a[100];
int b[100];//存权值
int n=0;//字符个数
char code[100];//存输入的二进制代码。
void CreateHuffmanTree(HuffmanTree &HT, char t[], int w[])
{
int m, s1, s2;
m=2*n-1; //总共需要2n-1个节点
HT=new HTNode[m+1];//开辟空间
for(int i=0; i<n; i++)//给叶子结点赋值。
{
HT[i].letter=t[i];
HT[i].weight=w[i];
}
for(int i=0; i<=m; i++)//初始化所有结点的父节点,左孩子、右孩子都为0.
{
HT[i].code='\0';
HT[i].parent=HT[i].lchild=HT[i].rchild=-1;
}
for(int i=n; i<m; i++)//处理每个非叶子结点
{
int min1,min2;
min1=min2=9999;
s1=s2=-1;
for(int k=0; k<=i-1; k++) //找到两个权值最小的结点作为左右子树的根节点构造新的二叉树。
{
if(HT[k].parent==-1)//只在尚未构造成二叉树的结点中查找。
{
if(HT[k].weight<min1)
{
min2=min1;
s2=s1;
min1=HT[k].weight;
s1=k;
}
else if(HT[k].weight<min2)
{
min2=HT[k].weight;
s2=k;
}
}
}
HT[s1].parent=i;HT[s2].parent=i;//将他们两个的父节点设置为 i;
HT[i].lchild=s1;HT[i].rchild=s2;//把这两个分别当作 结点i 的左右孩子。
HT[i].weight=HT[s1].weight+HT[s2].weight;//他们两个的双亲为他们两个的和。
HT[i].letter='#';
}
}
void CreatHuffmanCode(HuffmanTree HT)//编码。
{
FILE *fp;
int start, c, f;
int i;
char *cd=new char [n];
cd[n-1]='\0';
if((fp=fopen("OutMa.txt","w"))==NULL) //输出哈夫曼编码到文件
{
printf("打开输出文件失败。\n");
exit(0);
}
fprintf(fp,"每个字符对应的哈夫曼编码:\n");
cout<<endl<<"每个字符对应的哈夫曼编码为:"<<endl;
for(i=0; i<n; i++)
{
start=n-1;
c=i;
f=HT[i].parent;
while(f!=-1)
{
start--;
if(HT[f].lchild==c)
{
cd[start]='0';
}
else
{
cd[start]='1';
}
c=f;
f=HT[f].parent;
}
HT[i].code=new char[n-start];
strcpy(HT[i].code,&cd[start]);
cout<<HT[i].letter<<": "<<HT[i].code<<endl;
fprintf(fp,"%c %s\n",HT[i].letter,HT[i].code);
}
fprintf(fp,"编码完成!\n");
fclose(fp);
delete cd;
}
void ReadData()//从文件中读取权值
{
FILE *fp1;
if(NULL== (fp1=fopen("data.txt","r")))
{
cout<<"error"<<endl;
exit(1);
}
int i=0;
while(fscanf(fp1,"%c%d",&a[i],&b[i])!=EOF)
n++,i++;
n--;
// memset(b,0,sizeof(b));
// char k;
// n=27;
// while(fscanf(fp1,"%c",&k)!=EOF)
// {
// for(int i=0;i<27;i++)
// {
// if(k==a[i])
// b[i]++;
// }
// }
fclose(fp1);
cout<<"各个数据及其对应权值为:"<<endl;
for(int i=0; i<n; i++)
{
cout<<a[i]<<":"<<b[i]<<" ";
if(i&&(i+1)%3==0)
cout<<endl<<endl;
}
}
void Yima(HuffmanTree HT,char cod[],int b) //译码
{
FILE *fp;
if((fp=fopen("Translate.txt","w"))==NULL)
{
cout<<"打开翻译文件失败。"<<endl;
exit(0);
}
char sen[100];
char temp[50];
char blank[]=" "; //空白字符串
int t=0;
int s=0;
int xx=0;
for(int i=0; i<b; i++)
{
temp[t++]=cod[i]; //读取字符
temp[t] = '\0';
for(int j=0; j<n; j++) //依次与所有字符编码开始匹配
{
if (!strcmp(HT[j].code,temp)) //匹配成功
{
sen[s]=HT[j].letter; //将字符保存到sen中
s++;
xx+=t;//用于寻找出错的位数,如果有某一位没有匹配则记录下来。
strcpy(temp,blank);
t=0;
break;
}
}
}
if(t==0) //t如果被置空了,表示都匹配出来了,打印译码
{
sen[s]='\0';
cout<<"译码为:"<<endl<<sen<<endl;
fprintf(fp,"%s",sen);
fclose(fp);
}
else //t如果没有被置空 , 源码无法被完全匹配
{
cout<<"二进制源码有错,不存在此编码,从第"<<xx+1<<"位开始"<<endl;
}
}
void InCode(HuffmanTree HT)
{
cout<<"译码:"<<endl;
int x,k,symbol;
char p;
while(1)
{
cout<<"请输入要译码的二进制字符串,输入'#'结束:"<<endl;
x=1;//判断是否有非法字符只能是0 1
k=0;//作为循环变量来使code【k】=输入的字符
symbol=1;//判断是否输入结束
while(symbol)//输入二进制代码。
{
cin>>p;
if(p!='1'&&p!='0'&&p!='#') //若存在其它字符,x设为0,表示输入的不是二进制数。
{
x=0;
}
code[k]=p;
k++;
if(p=='#')
symbol=0; //#号结束标志
}
if(x==1)
Yima(HT,code,k-1); //进行译码
else
{
cout<<"有非法字符!"<<endl;
}
cout<<"输入Y继续进行编码,其他任意键退出译码:"<<endl;
cin>>p;
if(p=='y'||p=='Y')
continue;
else
break;
}
}
void PrintHF1(HuffmanTree HT,int k,string ss)
{
ss+=" ";
if(HT[k].lchild==-1||HT[k].rchild==-1)
{
cout<<ss;
cout<<HT[k].letter<<endl;
return;
}
PrintHF1(HT,HT[k].rchild,ss);
cout<<ss;
cout<<HT[k].weight<<endl;
PrintHF1(HT,HT[k].lchild,ss);
}
void PrintHF2(HuffmanTree HT,int k)
{
if(HT)
{
if(HT[k].letter=='#')
cout<<HT[k].weight;
else
cout<<HT[k].letter;
if(HT[k].lchild!=-1||HT[k].rchild!=-1)
{
cout<<"(";
PrintHF2(HT,HT[k].lchild);
cout<<",";
PrintHF2(HT,HT[k].rchild);
cout<<")";
}
}
}
void menu()
{
cout<<endl<<endl<<endl<<endl<<" 哈夫曼编码/译码器 "<<endl;
cout<<" ************************************************************"<<endl;
cout<<" * *"<<endl;
cout<<" * 请输入以下数字选择功能 *"<<endl;
cout<<" * *"<<endl;
cout<<" * 1:从文件中读取数据 *"<<endl;
cout<<" * 2:建立哈夫曼树 *"<<endl;
cout<<" * 3:显示哈夫曼树 *"<<endl;
cout<<" * 4:对哈夫曼树进行编码 *"<<endl;
cout<<" * 5:对输入代码进行译码 *"<<endl;
cout<<" * 输入其他字符退出系统 *"<<endl;
cout<<" * *"<<endl;
cout<<" ************************************************************"<<endl<<" 请输入数字:";
}
int main()
{
char m;
HuffmanTree HT;
menu();
int p=2*n-1;
while(m!=5)
{
{
cin>>m;
if(m=='1')
{
system("cls");
ReadData();//从文件中读取数据。
cout<<"读入数据成功!"<<endl<<"继续输入数字选择功能:"<<endl;
}
else if(m=='2')
{
system("cls");
CreateHuffmanTree(HT, a, b);//建立哈夫曼树。
cout<<"建立哈夫曼树成功!"<<endl<<"继续输入数字选择功能:"<<endl;
}
else if(m=='4')
{
system("cls");
CreatHuffmanCode(HT);//对哈夫曼树进行编码。
cout<<"哈夫曼编码成功!可打开OutMa.txt查看。"<<endl<<"继续输入数字选择功能:"<<endl;
}
else if(m=='5')
{
system("cls");
InCode(HT);//对输入代码进行译码。
cout<<"译码结果同时存入Translate.txt文件,可打开查看"<<endl<<"输入6退出系统。"<<endl;
}
else if(m=='3')
{
system("cls");
int i=0;
while(i>=0)//找到根节点
{
if(HT[i].parent==-1)
break;
else
{
i++;
continue;
}
}
cout<<"凹入法显示哈夫曼树:"<<endl;
string ss="";
PrintHF1(HT,i,ss);
cout<<"括号法显示哈夫曼树:"<<endl;
PrintHF2(HT,i);
cout<<endl<<"继续输入数字选择功能:"<<endl;
}
else
break;
}
}
return 0;
}