哈夫曼编码/译码系统(树应用)

[问题描述]

利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。

[实现提示]

在本例中设置发送者和接受者两个功能,

发送者的功能包括:

①输入待传送的字符信息;

②统计字符信息中出现的字符种类数和各字符出现的次数(频率);

②根据字符的种类数和各自出现的次数建立哈夫曼树;

③利用以上哈夫曼树求出各字符的哈夫曼编码;

④将字符信息转换成对应的编码信息进行传送。

接受者的功能包括:

①接收发送者传送来的编码信息;

②利用上述哈夫曼树对编码信息进行翻译,即将编码信息还原成发送前的字符信息。

从以上分析可发现,在本例中的主要算法有三个:

(1)哈夫曼树的建立;

(2)哈夫曼编码的生成;

(3)对编码信息的翻译。

#include<cstdio>
#include<iostream>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;

const int Max_n=26;
const int Max_m=8;
int n=0;
string str;
char strcode[100];//strcode存放编码信息

//结构体构造字符权值(即次数) 
typedef struct {
	char a;
	int weight;
}DataType;

DataType Data[Max_n];

//Huffman的存储表示 
typedef struct {
	DataType data;//结点信息 
	int parent,lchild,rchild; 
}HTNode,*HuffmanTree;

//输入并统计字符串信息,这里按照出现顺序打印 
void PrintStrMess(){
	cout<<">>请输入您需要统计的字符串:";
	cin>>str;
	int a[Max_n];
    memset(a,0,sizeof(a));
	int len=str.length();
	int j=0;
	for(int i=0;i<len;i++){
	 	a[str[i]-'a']++;
	 	if(a[str[i]-'a']==1)
	 		Data[j++].a=str[i];
	} 
	for(int i=0;i<j;i++){
		Data[i].weight=a[Data[i].a-'a'];
	}
	cout<<"字符串中出现了"<<j<<"种字符"<<endl; 
	for(int i=0;i<j;i++){
		cout<<Data[i].a<<"出现的次数为:"<<Data[i].weight<<endl; 
	}
	n=j;
	cout<<endl;
	cout<<">>任意键返回主菜单."<<endl;
	system("pause");
	system("cls");
}

/*找到最小的两个,传引用*/
void Select(HuffmanTree &HT,int n,int &mmin1,int &mmin2){
	int start;//记录遍历起点 
	for(int i=1;i<=n;i++){
		if(HT[i].parent==0){
			start=i;
			break;
		}
	}
	HT[mmin1].data.weight=inf;
	for(int i=start;i<=n;i++){
		if(HT[i].data.weight<=HT[mmin1].data.weight&&HT[i].parent==0)
			mmin1=i;//遍历,如果权值比当前值小,就更新 
	}
	for(int i=start;i<=n;i++){
		if(HT[i].data.weight<=HT[mmin2].data.weight&&HT[i].parent==0&&i!=mmin1){
				mmin2=i;
		}	
	}
}

/*创建Huffman树*/
void CreatHuffmanTree(HuffmanTree &HT,int n){
	if(n<=1) return ;
	HT=new HTNode[2*n];//0号单元未使用,所以需要动态分配m+1个单元,HT[m]表示根结点
	//初始化结点 
	for(int i=1;i<=2*n-1;i++){
		HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;
		HT[i].data.a=Data[i-1].a;HT[i].data.weight=Data[i-1].weight;//Data从0开始存入信息 
	} 
	for(int i=n+1;i<=2*n-1;i++){
		int mmin1=0,mmin2=0;//下标
		Select(HT,i-1,mmin1,mmin2);
		HT[mmin1].parent=i;HT[mmin2].parent=i;//找到的两个值标记双亲为i,表示这两个值已经处理过 
		HT[i].lchild=mmin1;HT[i].rchild=mmin2;//mmin1,mmin2分别为双亲的左右孩子
		HT[i].data.weight=HT[mmin1].data.weight+HT[mmin2].data.weight;//双亲权值是左右孩子权值之和 
	}
}

typedef char **HuffmanCode;//动态分配数组存储哈夫曼编码表 

/*哈夫曼编码*/
void CreatHuffmanCode(HuffmanTree &HT,HuffmanCode &HC,int n){
	//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中 
	HC=new char*[n+1];//为HC分配n个字符编码的空间,0号位置不用 
	char *cd=new char[n];
	cd[n-1]='\0';//编码结束符
	for(int i=1;i<=n;i++){//组个字符求编码
		int start=n-1;//记录编码在cd中存放的位置,初始时指向最后
		int c=i;int f=HT[i].parent;//c记录从叶子结点向上回溯至根节点的下标,f记录i的双亲的下标
		while(f!=0){//知道向上没有双亲 
			start--;//向上走一次,start前移一次
			if(HT[f].lchild==c) cd[start]='0'; //左孩子编码即为0
			else cd[start]='1'; //右孩子编码即为1
			c=f;f=HT[f].parent; //继续向上找 
		}
		HC[i]=new char[n-start];//第i个编码字符分配空间
		strcpy(HC[i],&cd[start]); 
	} 
	delete cd;
}

/*创建Huffman树,求哈夫曼编码*/
void Creat(HuffmanTree &HT,HuffmanCode &HC){
	CreatHuffmanTree(HT,n);
	cout<<"哈夫曼树创建成功!"<<endl;	
	CreatHuffmanCode(HT,HC,n);  
	cout<<"成功求出哈夫曼编码,任意键继续后续操作!"<<endl;
	system("pause");
	system("cls"); 
} 

/*打印输入的字符串对应的编码*/
void PrintStrCode(HuffmanTree &HT,HuffmanCode &HC){
	for(int i=1;i<=n;i++){
		cout<<HT[i].data.a<<"所对应的哈夫编码为:"<<HC[i]<<endl; 
	}
	cout<<endl; 
	cout<<"----------------------------------------------------------"<<endl;
	cout<<endl;
	strcode[0]='\0';
	cout<<str<<"对应的编码为(即为发送者发送编码):";
	int len=str.length();
	for(int i=0;i<len;i++){
		for(int j=1;j<=n;j++){
			if(str[i]==HT[j].data.a){
				cout<<HC[j];
				strcat(strcode,HC[j]);
				break;
			}
		}
	} 
	cout<<endl;
	cout<<endl;
	cout<<">>按任意键进入主界面选择解码操作:"<<endl;
	system("pause");
	system("cls");
}

/*译码*/
void TracStrCode(HuffmanTree &HT){
	cout<<"接收到的信息为:"<<strcode<<endl;
	int root=2*n-1;//根结点
	int i=0;//向下搜索的方向(0为左,1为右) 
	cout<<"接收到的信息对应的字符为:";
	while(strcode[i]!='\0'){
		if(strcode[i]=='0')
			root=HT[root].lchild;
		else
			root=HT[root].rchild;
		if(!HT[root].lchild&&!HT[root].rchild){
			printf("%c",HT[root].data.a);
			root=2*n-1;
		}
		i++;
	} 
	cout<<endl;
	cout<<"(接收者正确字符串应为:"<<str<<")"<<endl;
	cout<<endl;
	cout<<"恭喜你完成一次发送接收信息的操作"<<endl;
	cout<<">>按任意键进入主界面进行新的操作"<<endl; 
	system("pause");
	system("cls");
} 
void Menu(HuffmanTree &HT,HuffmanCode &HC){
	cout<<"----------------------------------------------------------"<<endl;
	cout<<"     1.输入字符串统计字符串信息(类数,各字符出现的频数)"<<endl;
	cout<<"     2.建立哈夫曼树并且求出哈夫曼编码"<<endl; 
	cout<<"     3.打印各字符的哈弗编码及发送的编码信息"<<endl;
	cout<<"     4.接收的编码信息及编码结果(打印字符)"<<endl;
	cout<<"     other(其他输入).退出"<<endl;
	cout<<"----------------------------------------------------------"<<endl;
	//(输入,统计,发送,接收是完整的一次)
	cout<<endl<<">>请您按照顺序依次选择功能:";
	int chose;
	cin>>chose;
	switch(chose){
		case 1: {
			PrintStrMess();
			Menu(HT,HC); 
		}
		case 2: {
			Creat(HT,HC);
			Menu(HT,HC);
		}
		case 3: {
			PrintStrCode(HT,HC);
			Menu(HT,HC);
		}
		case 4: {
			TracStrCode(HT);
			Menu(HT,HC); 
		}
		default : exit(0);
	} 
} 
int main(){
	HuffmanTree HT;
	HuffmanCode HC;
	Menu(HT,HC);
	return 0;
}

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值