要求:
给定报文中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赫夫曼编码串,翻译出对应的原文。
初始化时输入26个字符:a b c d e f g h i g k l m n o p q r s t u v w x y z (最后有一个空格作为第27个字符,不用输入)
对应权重: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
代码及释义如下:
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <malloc.h>
#include <stdio.h>
using namespace std;
#define MAXLEAFNUM 27//带权值的叶子节点数(需要编码的字符数)
#define M 2*MAXLEAFNUM-1//n个叶子节点构造的哈夫曼树有2n-1个结点
#define MAX 10000
typedef struct node{
int weight;//权值
int parent;//结点的父结点的下标,为0表示无父结点
int lchild,rchild;//结点的左右孩子结点的下标,为0表示无孩子结点
}HTNode;
typedef HTNode HuffmanTree[M+1];//数组存储赫夫曼树
typedef char* HuffmanCode[MAXLEAFNUM + 1];//数组存储赫夫曼编码表
//选择parent为0的且权值最小的2个结点s1,s2
void select(HuffmanTree &HT, int k, int &s1, int &s2){
//设s1对应的权值总小于等于s2对应的权值
int tmp = MAX, tmpi = 0;
for(int i = 1; i <= k; i++){
if(HT[i].parent==0){//parent为0(还没有放入树中的结点)
if(tmp > HT[i].weight){
tmp = HT[i].weight;//tmp最后为最小的weight
tmpi = i;
}
}
}
s1 = tmpi;
tmp = MAX;
tmpi = 0;
for(int i = 1; i <= k; i++){
if((HT[i].parent==0) && i!=s1){//s1和s2下标不能相同
if(tmp > HT[i].weight){
tmp = HT[i].weight;
tmpi = i;
}
}
}
s2 = tmpi;
}
//1.输入HuffmanTree的参数
void Input_Char(char * ch)
{
cout<<"请输入26个字符(第27个字符为空格):"<<endl;
for(int i = 1; i <= MAXLEAFNUM-1; i++){
cin>>ch[i];
}
ch[MAXLEAFNUM]=' ';
}
//2.初始化HuffmanTree参数
void initializer(int * w)
{
cout<<"请输入27个字符对应的权值:"<<endl;
for(int i = 1; i <= MAXLEAFNUM; i++){
cin>>w[i];
}
//w里第i个权值对应的是ch里第i个字符元素
}
//3.创建HuffmanTree和编码表
void createHuffmanTree(HuffmanTree &HT, int *w, char *ch)
{
//对树赋初值
for(int i = 1; i <= MAXLEAFNUM; i++){
HT[i].weight = w[i];
HT[i].lchild = 0;
HT[i].parent = 0;
HT[i].rchild = 0;
}
for(int i=MAXLEAFNUM+1; i <=M; i++){//HT后m-MAXLEAFNUM个分量存储中间结点,最后一个分量是整棵树的根节点
HT[i].weight = 0;
HT[i].lchild = 0;
HT[i].parent = 0;
HT[i].rchild = 0;
}
//用哈夫曼算法构建哈夫曼树,即创建HT的后m-MAXLEAFNUM个结点的过程,直至创建出根节点。
for(int i = MAXLEAFNUM+1; i <= M; i++){
int s1, s2;
select(HT, i-1, s1, s2);//在HT[1...i-1]里选择parent为0的且权值最小的2结点
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;
}
cout<<"创建HuffmanTree成功!"<<endl;
return;
}
//为每个字符求解哈夫曼编码,从叶子到根逆向求解每个字符的哈夫曼编码
void encodingHuffmanCode(HuffmanTree &HT, HuffmanCode &HC){
char tmp[MAXLEAFNUM];
tmp[MAXLEAFNUM-1] = '\0';//编码的结束符
int start, c, f;
for(int i = 1; i <= MAXLEAFNUM; i++)
{//对于第i个待编码字符即第i个带权值的叶子节点
start = MAXLEAFNUM-1;//编码生成以后,start将指向编码的起始位置
c = i;
f = HT[i].parent;
while(f!=0){//f!=0,即f不能是根节点的父节点
if(HT[f].lchild == c){
tmp[--start] = '0';
}else{
tmp[--start] = '1';
}
c = f;
f = HT[f].parent;
}
HC[i] = (char *)malloc((MAXLEAFNUM-start)*sizeof(char));//每次tmp的后MAXLEAFNUM-start个位置有编码存在
strcpy(HC[i], &tmp[start]);//将tmp的后MAXLEAFNUM-start个元素分给H[i]指向的的字符串
}
cout<<"创建编码表成功"<<endl;
return;
}
//4.输出编码表
void printHuffmanCoding(HuffmanCode HC, char ch[]){
for(int i = 1; i <= MAXLEAFNUM; i++){
cout<<ch[i]<<":"<< HC[i]<<endl;
}
}
//5.输入编码,并翻译为字符
void decoding_num(HuffmanTree HT, char *ch, char *result){
int p = M;//HT的最后一个节点是根节点,前MAXLEAFNUM个节点是叶子节点(从根节点开始编码)
int i = 0;//指示测试串中的第i个字符
int j = 0;//指示结果串中的第j个字符
string testDecodingStr;
cout<<"接收端:输入编码:";
cin>>testDecodingStr;
int len=testDecodingStr.length();
while(i<len){
if(testDecodingStr[i] == '0'){
p = HT[p].lchild;//向左,左孩子记录的在数组的位置
}
if(testDecodingStr[i] == '1'){
p = HT[p].rchild;//向右,右孩子记录的在数组的位置
}
if(p <= MAXLEAFNUM){//p<=MAXLEAFNUM则表明p为叶子节点
result[j] = ch[p];
j++;
p = M;//p重新指向根节点
}
i++;
}
result[j] = '\0';//结果串的结束符
}
//6.输入字符,并实现转码
void decoding_char(HuffmanCode HC,char *ch)
{
char str;
cout<<"发送端:请输入字符:";
cin.ignore();//忽略缓冲端的字符
str=getchar();
cout<<"输出:";
while(str!='\n')
{
if(str==' ')
{
cout<<HC[27];//空格存在数组的第27个
}
else
{
if(str>='A'&&str<='Z')
{
str+=32;//大写转小写
}
for(int j=1;j<=26;j++)
{
if(str==ch[j])
{
cout<<HC[j];
}
}
}
str=getchar();
}
cout<<endl;
}
int main()
{
cout<<"可执行的操作有:"<<endl;
cout<<setfill('*')<<setw(80)<<' '<<endl;//如何形式化输出
cout<<setfill('*')<<setw(15)<<" "<<"1.输入HuffmanTree的参数 "<<setfill(' ')<<setw(26)<<' '<<setfill('*')<<setw(16)<<" "<<endl;
cout<<setfill('*')<<setw(15)<<" "<<"2.初始化HuffmanTree参数。(含有26字母及空格) "<<setfill(' ')<<setw(5)<<' '<<setfill('*')<<setw(16)<<" "<<endl;
cout<<setfill('*')<<setw(15)<<" "<<"3.创建HuffmanTree和编码表。 "<<setfill(' ')<<setw(22)<<' '<<setfill('*')<<setw(16)<<" "<<endl;
cout<<setfill('*')<<setw(15)<<" "<<"4.输出编码表 "<<setfill(' ')<<setw(37)<<' '<<setfill('*')<<setw(16)<<" "<<endl;
cout<<setfill('*')<<setw(15)<<" "<<"5.输入编码,并翻译为字符。 "<<setfill(' ')<<setw(23)<<' '<<setfill('*')<<setw(16)<<" "<<endl;
cout<<setfill('*')<<setw(15)<<" "<<"6.输入字符,并实现转码 "<<setfill(' ')<<setw(27)<<' '<<setfill('*')<<setw(16)<<" "<<endl;
cout<<setfill('*')<<setw(15)<<" "<<"7.退出 "<<setfill(' ')<<setw(43)<<' '<<setfill('*')<<setw(16)<<" "<<endl;
cout<<setfill('*')<<setw(80)<<' '<<endl;
cout<<"》》》》》》》》》》》》请输入你的选择:";
int c;
cin>>c;
while(true)
{
HuffmanTree HT;
char ch[MAXLEAFNUM+1];
int w[MAXLEAFNUM+1];
HuffmanCode HC;
char result[30];
if(c==1)
{
Input_Char(ch);
}
else if(c==2)
{
initializer( w);
}
else if(c==3)
{
createHuffmanTree(HT, w, ch);
encodingHuffmanCode(HT,HC);
}
else if(c==4)
{
printHuffmanCoding(HC, ch);
}
else if(c==5)
{
decoding_num(HT,ch,result);
int i=0;
cout<<"输出:";
while(result[i]!='\0')
{
cout<<result[i];
i++;
}
cout<<endl;
}
else if(c==6)
{
decoding_char(HC,ch);
}
else if(c==7)
{
break;
}
else
{
cout<<"输入的选择不存在,请重新输入"<<endl;
}
cout<<"》》》》》》》》》》》》请输入你的选择:";
cin>>c;
}
return 0;
}
测试示例:
1.模拟发送端
输入:I love you
输出:01101111011110011100000010111100011100100001
2.模拟接收端 输入
输入:01101101111011000111111010111101101001100001
输出:it is a dog
测试结果: