哈夫曼编码,即根据字母出现的频率对其进行编码,适用于频率参差不齐的情况。
哈夫曼树的建树过程(重点)
举个例子 A B C D E五个字母出现的频率依次为5 4 1 3 8
先把他们存储在一个数组里,因为还要相加取父类,所以一共需要2*5-1=9个位置(这里0位置弃用,以便填写下标)
初始化1-5的权值
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
weight | 0 | 5 | 4 | 1 | 3 | 8 | 0 | 0 | 0 | 0 |
parent | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
lchild | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
rchild | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
第一次遍历,找到最小的两个权值(1,3)下标为3,4,将这两个权值相加赋值到下标为6的weight上,并且将 下标3 和 4 的parent改为 新建权值的下标 将 新建节点的左右子树 改为两个节点的 下标
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
weight | 0 | 5 | 4 | 1 | 3 | 8 | 4 | 0 | 0 | 0 |
parent | 0 | 0 | 0 | 6 | 6 | 0 | 0 | 0 | 0 | 0 |
lchild | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 |
rchild | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 0 |
第二次遍历,找到最小的两个权值且parent为0(4,4)的下标2,6,同理
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
weight | 0 | 5 | 4 | 1 | 3 | 8 | 4 | 8 | 0 | 0 |
parent | 0 | 0 | 7 | 6 | 6 | 0 | 7 | 0 | 0 | 0 |
lchild | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 2 | 0 | 0 |
rchild | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 6 | 0 | 0 |
…
最终为
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
weight | 0 | 5 | 4 | 1 | 3 | 8 | 4 | 8 | 13 | 21 |
parent | 0 | 8 | 7 | 6 | 6 | 8 | 7 | 9 | 9 | 0 |
lchild | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 2 | 1 | 7 |
rchild | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 6 | 5 | 8 |
哈夫曼树到这就构建好了
哈夫曼编码的生成(重点)
然后求每个字符的哈夫曼编码
举个例子,就上面建成的树求5的哈夫曼编码
从5这个节点开始找他的父类,如果父类不为0(父类为0即代表根节点)判断5是他父类的左孩子还是右孩子,左孩子就存储0,右孩子就存储1(注意是 逆序存储,也可以用栈存储)。然后再以5的父类为参照,找他的父类判断是否为0…(循环)
最终得出5的哈夫曼编码是11
如题:
给定报文中26个字母a-z及空格的出现频率{64, 13, 22, 32, 103, 21, 15, 47, 57, 1, 5, 32, 20, 57, 63, 15, 1, 48, 51, 80, 23, 8, 18, 1, 16, 1, 168},构建哈夫曼树并为这27个字符编制哈夫曼编码,并输出。模拟发送端,从键盘输入字符串,以%为结束标记,在屏幕上输出输入串的编码;模拟接收端,从键盘上输入0-1哈夫曼编码串,翻译出对应的原文。
头文件及结构体部分
#include //C++万能头文件
#define inf 0x3f3f3f3f//将inf设为最大(至于为什么是0x3f3f3f3f可以自行百度)
using namespace std;
typedef struct{
int weight;//权值
int parent,lchild,rchild;//父,左孩子和右孩子
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
typedef char **HuffmanCode;//动态分配数组存储哈夫曼编码表,两个*号是因为是二维数组
输入参数
void HuffmanCout(int *a)//传入a的首地址
{
for(int i=0;i<27;i++)
cin>>a[i];//输入26个英文字母及空格出现的频率
}
初始化HuffmanTree参数
void HuffmanInit(HuffmanTree &HT,int *w,int n)//w存放n个字符的权值
{
int i;
HuffmanTree p;
if(n<=1)
return ;
int m=2*n-1;//共m个元素,即n + n+1
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0单元未用,所以申请m+1个空间
for(p=HT+1,i=1;i<=n;++i,++p,++w)//传入的p和w都是数组的首地址,++p和++w即右移一位
*p={*w,0,0,0};//1~n依次存入对应的权值w
for(;i<=m;++i,++p)
*p={0,0,0,0};//n+1~m依次初始化0
}
创建HuffmanTree和编码表
void Select(HuffmanTree &HT,int end,int &s1,int &s2)//选择函数,选择数组中权值最小的两个元素的下标
{
int min1=inf,min2=inf;//初始化min1和min2为最大值
for(int i=1;i<=end;++i)
{
if(HT[i].parent==0 && HT[i].weight<min1)//HT[i].parent==0即没有使用的元素
{
min1=HT[i].weight;
s1=i;//找到权值最小元素的下标
}
}
for(int i=1;i<=end;++i)
{
if(HT[i].parent==0 && HT[i].weight<min2 && i!=s1)//i!=s1即第二小
{
min2=HT[i].weight;
s2=i;//找到权值第二小元素的下标
}
}
}
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n)
{
int s1,s2;//为后面最小的两个权值
int m=2*n-1;
for(int i=n+1;i<=m;++i)//构建哈夫曼树
{
Select(HT,i-1,s1,s2);//找最小的两个数
HT[i].weight=HT[s1].weight+HT[s2].weight;//下标i的权值,两孩子权值之和
HT[s1].parent=i;
HT[s2].parent=i;//两个孩子的母亲的下标都是i
HT[i].lchild=s1;//下标i的左孩子,最小
HT[i].rchild=s2;//右孩子,第二小
}
//从叶子到根逆向求每个字符的哈夫曼编码
HC=(HuffmanCode)malloc((n+1)*sizeof(char *));//分配n个字符编码的头指针向量
char *cd;
cd=(char *)malloc(n*sizeof(char));//创建字符型数组cd
cd[n-1]='\0';//编码结束符
for(int i=1;i<=n;++i)//逐个字符求哈夫曼编码
{
int start=n-1;//编码结束符的位置,没错,逆序存储
for(int c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//从叶子到根逆向求编码
{//初始c=i,f=i的父;循环完c=c的父,f=f的父(往后一辈);直到f!=0,根节点了
if(HT[f].lchild==c) cd[--start]='0';//如果c是f的左孩子,逆序存储一个0
else cd[--start]='1';//否则就逆序存储一个1
}
HC[i]=(char *)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间,(n-start)个元素空间,即从start到n。
strcpy(HC[i],&cd[start]);//strcpy是字符串copy函数,即从数组cd的start位置开始到结束这段区间内的字符赋值给HC[i]。
}
free(cd);//释放cd
}
输出编码表
void showHuffmanCode(HuffmanCode HC) //显示每个字符的赫夫曼编码
{
char c ;
for(int i=1;i<=27;++i){
if(i!=27)//前26个是字母
{
c=i+'a'-1;
cout<<c<<"的赫夫曼编码是:";
}
else//第27个是空格
{
cout<<"空格的赫夫曼编码是:";
}
for(int j=0;HC[i][j]!='\0';++j)
{//依次输出数组HC[i]里的字符(0或1),直到末尾的'\0'不输出
cout<<HC[i][j];
}
//注意:这里的HC数组是二维数组
cout<<endl;
}
}
输入编码,翻译为字符(模拟接收端)
void TanserHuffmanCode(HuffmanCode HC,string s)//s为编码
{
string ss="",s1="";//初始化ss和s1均为空字符串
string t[27];//27个t字符串存26个字母和空格的编码
for(int i=0;i<27;++i)
{
t[i]="";
for(int k=0;HC[i+1][k]!='\0';++k)
t[i]+=HC[i+1][k];//就是把编码表逐一赋值给t
// cout<<' '<
}
for(int i=0;i<s.size();++i)//字符串匹配算法,非KMP,最普通的暴力算法
{
ss+=s[i];//取编码元素赋值给ss,+号代表字符串连接符
for(int j=0;j<27;++j)//注意j从0开始(因为t从0开始,这样写方便)
{
if(ss==t[j])//与第j个t进行匹配
{//匹配成功,即找到相应编码对应的字符
ss="";//ss初始化为空,重新选取编码
if(j!=26)//不是第27个字符就存储对应的字母
s1+=j+'a';
else//空格情况
s1+=' ';
}
//匹配不成功,++j,与下一个t匹配
}
//若都不成功,则++i,取下一个编码元素,再进行匹配
}
cout<<s1<<endl;//输出字符串s1,破译完成
}
输入字符,转换成编码(模拟发送端)
void TanserString(HuffmanCode HC,string s)//s为字符
{
string ss;
for(int i=0;i<s.length();++i)
{
if(s[i]>='A' && s[i]<='Z')
s[i]+=32;//大写字母全部转换为小写,因为此编码程序不区分大小写,统统按小写
else if(s[i]==' ')
s[i]='z'+1;//空格就转换为ASCII码表z的下一位
}
for(int i=0;i<s.length();++i)
{
for(int j=0;HC[s[i]-'a'+1][j]!='\0';++j)//依次遍历全部字符
ss+=HC[s[i]-'a'+1][j];//每个字符的编码都贴到ss上
}
cout<<ss<<endl;//输出ss,即字符串的编码,加密成功
}
#include
#define inf 0x3f3f3f3f
using namespace std;
typedef struct{
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;
void HuffmanCout(int *a)
{
for(int i=0;i<27;i++)
cin>>a[i];
}
void HuffmanInit(HuffmanTree &HT,int *w,int n)
{
int i;
HuffmanTree p;
if(n<=1)
return ;
int m=2*n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(p=HT+1,i=1;i<=n;++i,++p,++w)
*p={*w,0,0,0};
for(;i<=m;++i,++p)
*p={0,0,0,0};
}
void Select(HuffmanTree &HT,int end,int &s1,int &s2)
{
int min1=inf,min2=inf;
for(int i=1;i<=end;++i)
{
if(HT[i].parent==0 && HT[i].weight<min1)
{
min1=HT[i].weight;
s1=i;
}
}
for(int i=1;i<=end;++i)
{
if(HT[i].parent==0 && HT[i].weight<min2 && i!=s1)
{
min2=HT[i].weight;
s2=i;
}
}
}
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n)
{
int s1,s2;
int m=2*n-1;
for(int i=n+1;i<=m;++i)
{
Select(HT,i-1,s1,s2);
HT[i].weight=HT[s1].weight+HT[s2].weight;
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2;
}
HC=(HuffmanCode)malloc((n+1)*sizeof(char *));
char *cd;
cd=(char *)malloc(n*sizeof(char));
cd[n-1]='\0';
for(int i=1;i<=n;++i)
{
int start=n-1;
for(int c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)
{
if(HT[f].lchild==c) cd[--start]='0';
else cd[--start]='1';
HC[i]=(char *)malloc((n-start)*sizeof(char));
strcpy(HC[i],&cd[start]);
}
free(cd);
}
}
void showHuffmanCode(HuffmanCode HC)
{
char c ;
for(int i=1;i<=27;++i){
if(i!=27)
{
c=i+'a'-1;
cout<<c<<"的赫夫曼编码是:";
}
else
{
cout<<"空格的赫夫曼编码是:";
}
for(int j=0;HC[i][j]!='\0';++j)
{
cout<<HC[i][j];
}
cout<<endl;
}
}
void TanserHuffmanCode(HuffmanCode HC,string s)
{
string ss="",s1="";
string t[27];
for(int i=0;i<27;++i)
{
t[i]="";
for(int k=0;HC[i+1][k]!='\0';++k)
t[i]+=HC[i+1][k];
// cout<<' '<
}
for(int i=0;i<s.size();++i)
{
ss+=s[i];
for(int j=0;j<27;++j)
{
if(ss==t[j])
{
ss="";
if(j!=26)
s1+=j+'a';
else
s1+=' ';
}
}
}
cout<<s1<<endl;
}
void TanserString(HuffmanCode HC,string s)
{
string ss;
for(int i=0;i<s.length();++i)
{
if(s[i]>='A' && s[i]<='Z')
s[i]+=32;
else if(s[i]==' ')
s[i]='z'+1;
}
for(int i=0;i<s.length();++i)
{
for(int j=0;HC[s[i]-'a'+1][j]!='\0';++j)
ss+=HC[s[i]-'a'+1][j];
}
cout<<ss<<endl;
}
void menu()
{
cout << "************************************************************" << endl;
cout << "******** 1.输入HuffmanTree的参数 ****" << endl;
cout << "******** 2.初始化HuffmanTree参数.《含有26字母及空格》 ****" << endl;
cout << "******** 3.创建HuffmanTree和编码表。 ****" << endl;
cout << "******** 4.输出编码表。 ****" << endl;
cout << "******** 5.输入编码,并翻译为字符。 ****" << endl;
cout << "******** 6.输入字符,并实现转码 ****" << endl;
cout << "******** 7.退出 ****" << endl;
cout << "************************************************************" << endl;
}
int main()
{
HuffmanTree HT;
HuffmanCode HC;
string s;
HC = (HuffmanCode) malloc ((27+1) * sizeof(char *));
for(int i=1;i<=28;i++)
HC[i]=(char *)malloc((27+1)*sizeof(char));
menu();
int a[27];
int order;
do
{
cout<<"请输入操作:"<<endl;
cin>>order;
switch(order)
{
case 1:
cout<<"请输入HuffmanTree的参数:"<<endl;
HuffmanCout(a);
cout<<"输入完毕"<<endl;
break;
case 2:
HuffmanInit(HT,a,27);
cout<<"HuffmanTree的参数初始化完成"<<endl;
break;
case 3:
HuffmanCoding(HT,HC,a,27);
cout<<"创建成功"<<endl;
break;
case 4:
showHuffmanCode(HC);
break;
case 5:
getchar();
cout<<"请输入HuffmanCode:";
getline(cin,s);
TanserHuffmanCode(HC,s);
break;
case 6:
getchar();
cout<<"请输入字符:";
getline(cin,s);
TanserString(HC,s);
break;
case 7:
cout<<"程序已退出"<<endl;
break;
default :
cout<<"输入违法"<<endl;
break;
}
}while(order!=7);
return 0;
}
//64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1 168
“所有的努力,不是为了让别人觉得你了不起,而是为了能让自己打心眼里看得起自己。”