描述
输入一串字符串,根据给定的字符串中字符出现的频率建立相应哈夫曼树,构造哈夫曼编码表,在此基础上可以对待压缩文件进行压缩(即编码),同时可以对压缩后的二进制编码文件进行解压(即译码)。
输入
多组数据,每组数据一行,为一个字符串(只考虑26个小写字母即可)。当输入字符串为“0”时,输入结束。
输出
每组数据输出2n+3行(n为输入串中字符类别的个数)。第一行为统计出来的字符出现频率(只输出存在的字符,格式为:字符:频度),每两组字符之间用一个空格分隔,字符按照ASCII码从小到大的顺序排列。第二行至第2n行为哈夫曼树的存储结构的终态(形如教材139页表5.2(b),一行当中的数据用空格分隔)。第2n+1行为每个字符的哈夫曼编码(只输出存在的字符,格式为:字符:编码),每两组字符之间用一个空格分隔,字符按照ASCII码从小到大的顺序排列。第2n+2行为编码后的字符串,第2n+3行为解码后的字符串(与输入的字符串相同)。
输入样例 1
aaaaaaabbbbbccdddd
aabccc
0
输出样例 1
a:7 b:5 c:2 d:4
1 7 7 0 0
2 5 6 0 0
3 2 5 0 0
4 4 5 0 0
5 6 6 3 4
6 11 7 2 5
7 18 0 1 6
a:0 b:10 c:110 d:111
00000001010101010110110111111111111
aaaaaaabbbbbccdddd
a:2 b:1 c:3
1 2 4 0 0
2 1 4 0 0
3 3 5 0 0
4 3 5 2 1
5 6 0 3 4
a:11 b:10 c:0
111110000
aabccc
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;int d;int fre[26];int kn=0;int k=0;char namelist[26];
typedef struct{
int vtag;
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char** HuffmanCode;
typedef struct{
int Freque;
HuffmanCode HCC;
char ch;
}Letter;
void Select(HuffmanTree HT,int len,int &s1,int &s2)
{
int i,min1=0x3f3f3f3f,min2=0x3f3f3f3f;//先赋予最大值
for(i=1;i<=len;i++)
{
if(HT[i].weight<min1&&HT[i].parent==0)
{
min1=HT[i].weight;
s1=i;
}
}
int temp=HT[s1].weight;//将原值存放起来,然后先赋予最大值,防止s1被重复选择
HT[s1].weight=0x3f3f3f3f;
for(i=1;i<=len;i++)
{
if(HT[i].weight<min2&&HT[i].parent==0)
{
min2=HT[i].weight;
s2=i;
}
}
HT[s1].weight=temp;//恢复原来的值
}
void CreateHuffmanTree(HuffmanTree &HT,int n)
{
int i;
if(n<=1) return;
int m=2*n-1;
HT=new HTNode[m+1];
for(i=1;i<=m;i++)
{
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(i=1;i<=n;i++)
HT[i].weight=fre[i-1];
int s1,s2;
for(i=n+1;i<=m;i++)
{
Select(HT,i-1,s1,s2);
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
}
void FrequeStats(int n[],string s)
{
int i=0;
while(s[i]!='\0')
{
int e=(int) s[i];
if(e>=97)
n[e-97]++;
i++;
}
d=i;
// cout<<"一共输入了"<<d<<"个字符"<<endl;
for(i=0;i<26;i++)
if(n[i])
{
kn++;
}
for(i=0;i<26;i++)
if(n[i])
{
char e=(char)(i+97);
cout<<e<<":"<<n[i];
if(k!=kn-1)cout<<" ";//多输出了一个
fre[k]=n[i];
namelist[k]=e;
k++;
}
//cout<<"一共有"<<k<<"个叶子结点"<<endl;
cout<<endl;
}
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
//从叶子到根逆向求每个字符的赫夫曼编码,存储在编码表HC中
int i,start,c,f;
HC=new char*[n+1]; //分配n个字符编码的头指针矢量
char *cd=new char[n]; //分配临时存放编码的动态数组空间
cd[n-1]='\0'; //编码结束符
for(i=1;i<=n;++i)
{ //逐个字符求赫夫曼编码
start=n-1; //start开始时指向最后,即编码结束符位置
c=i;
f=HT[i].parent; //f指向结点c的双亲结点
while(f!=0)
{ //从叶子结点开始向上回溯,直到根结点
--start; //回溯一次start向前指一个位置
if(HT[f].lchild==c)
cd[start]='0'; //结点c是f的左孩子,则生成代码0
else
cd[start]='1'; //结点c是f的右孩子,则生成代码1
c=f;
f=HT[f].parent; //继续向上回溯
} //求出第i个字符的编码
HC[i]=new char[n-start]; // 为第i 个字符编码分配空间
strcpy(HC[i], &cd[start]); //将求得的编码从临时空间cd复制到HC的当前行中
}
delete cd; //释放临时空间
} // CreatHuffanCode
void Show(HuffmanTree HT,HuffmanCode HC)
{
for(int i=1;i<=2*k-1;i++)
cout<<i<<" "<<HT[i].weight<<" "<<HT[i].parent<<" "<<HT[i].lchild<<" "<<HT[i].rchild<<endl;
for(int i=1;i<=k;i++)
{
cout<<namelist[i-1]<<":"<<HC[i];
if(i!=k)cout<<" ";//多输出了一个
}
cout<<endl;
}
void Tranfer(HuffmanCode HC,string s)
{
int i=0;
while(s[i])
{
int j=0;
while(j<26)
{
if(s[i]==namelist[j])//如果该字符与namelist中某个字符相等
cout<<HC[j+1];//输出其对应编码
j++;
}
i++;
}
cout<<endl;
i=0;
while(s[i])
{
char e;
e=s[i];
cout<<e;
i++;
}
cout<<endl;
}
void InitData(int fre[])
{
for(int i=0;i<26;i++)
fre[i]=0;
//memset(fre,0,sizeof(fre));//不能用这个,为什么?
memset(namelist,0,sizeof(namelist));
d=0;k=0;kn=0;
return;
}
int main()
{
while(1)
{
string s;
cin>>s;
if("0"==s) break;
InitData(fre);
FrequeStats(fre,s);
HuffmanTree HT;
HuffmanCode HC;
CreateHuffmanTree(HT,k);
CreatHuffmanCode(HT,HC,k);
Show(HT,HC);
Tranfer(HC,s);
}
return 0;
}