个人笔记--HuffmanTree哈夫曼树(赫夫曼树)及哈夫曼编码

哈夫曼树的构建及哈夫曼编码的生成与转换

哈夫曼编码,即根据字母出现的频率对其进行编码,适用于频率参差不齐的情况。

哈夫曼树的建树过程(重点)
举个例子 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

“所有的努力,不是为了让别人觉得你了不起,而是为了能让自己打心眼里看得起自己。”

你可能感兴趣的:(数据结构实验,c++)