哈夫曼树的编码和解码

     哈夫曼树的作用:在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。

哈夫曼树的结构

//创建一个树结点
typedef struct TreeNode{
	char data;     //结点元素
	int weight;    //树的权重
	int parent;    //双亲结点
	int lchild;    //左孩子
	int rchild;    //右孩子
}TreeNode;    

//创建一个树(顺序存储)
typedef struct HFTree{
	TreeNode* data;    
	int length;    //树的长度
}HFTree; 

初始化哈夫曼树

//初始化哈夫曼树
HFTree* initTree(int* weight,int length)
{
	HFTree* T=(HFTree*)malloc(sizeof(HFTree));
	T->data=(TreeNode*)malloc(sizeof(TreeNode)*(2*length-1));
	T->length=length;
	for(int i=0;i<length;i++)
	{
		T->data[i].weight=weight[i];
		T->data[i].parent=0;	//刚开始为空
		T->data[i].lchild=-1;	//刚开始为空
		T->data[i].rchild=-1;	//刚开始为空
	}
	for (int i = 0; i <length; i++)
	{
		//scanf("%c ",&HT[i].data);
		char a = getchar();
		if(a == '\n')												//遇到回车就结束
			break;
		else
			T->data[i].data = a;		//给每个结点赋予数据
	}
	return T;
}

选择权重最小的函数(便于哈夫曼树的创建)

//选择权重最小的函数
int* selectMin(HFTree* T)
{
	int min=10000;
	int secondmin=100000;
	int minIndex,secondminIndex;	//便于最小的两个权值无法在进行对比
	//找出最小的权值
	for(int i=0;i<T->length;i++)	
	{
		if(T->data[i].parent==0)
		{
			if(T->data[i].weight<min)
			{
				min=T->data[i].weight;
				minIndex=i; 	//便于后面求出第二小的权值
			}
		}
	}
	//求出第二小的权值
	for(int i=0;i<T->length;i++)
	{
		if(T->data[i].parent==0&&i!=minIndex)
		{
			if(T->data[i].weight<secondmin)
			{
				secondmin=T->data[i].weight;
				secondminIndex=i;
			}
		}
	}
	int* res=(int*)malloc(sizeof(int)*2); 	//放回两个值要用指针;
	res[0]=minIndex;
	res[1]=secondminIndex;
	return res;   //返回res指针
}

创建哈夫曼树

//创建哈夫曼树
void createHFTree(HFTree* T)
{
	int* res;
	int min;
	int secondmin;
	int len=T->length*2-1;	//总共有2n-1个结点
	for(int i=T->length;i<len;i++)
	{
		res=selectMin(T);
		min=res[0];
		secondmin=res[1];
		T->data[i].weight=T->data[min].weight+T->data[secondmin].weight;//双亲结点权重等于左右孩子权重之和
		T->data[i].lchild=min;
		T->data[i].rchild=secondmin;
		T->data[min].parent=i;
		T->data[secondmin].parent=i;
		T->length++;
		T->data[i].parent=0;
	}
}

 哈夫曼树的编码

​
void createHuffmanCode(HFTree* T, huffmanCode& HC, int n)
{
	HC = (huffmanCode)malloc(sizeof(huffmanCode) * n + 1);		
	/*申请len + 1个huffmanCode大小huffmanCode类型的临时空间
	因为下标是从一开始,所以我们要申请比结点多一个的结点,
	和哈夫曼树的结构对应,方便输出*/
	char* cd = (char*)malloc(sizeof(char) * n);		//申请n个char大小char类型的临时空间,这个临时数组记录每次遍历出来的编码
	int start = 0,c = 0,f = 0;				//start为cd数组记录下标,c初始为叶子结点下标,而后就是孩子结点的下标,f记录双亲结点的下标
	cd[n - 1] = '\0';					//这个就是给printf留着的,因为printf不会生成'\0',如果用puts就不用这句语句了
	for (int i = 0; i <n; i++)		//只要叶子结点的编码
	{
		start = n - 1;					//这句要赋值n的话,start--要写在判断后方
		c = i;							//指向叶子结点
		f = T->data[c].parent;			//指向双亲结点
		while (f != 0)					//根结点没有双亲
		{
			start--;
			if (T->data[f].lchild == c)			//是左孩子就是0,右孩子就为1
				cd[start] = '0';
			else
				cd[start] = '1';
			c = f; 
			f = T->data[c].parent;				//向根结点接近
		}
		HC[i] = (char*)malloc(sizeof(char) * (n - start));	//给数组里的数组申请n - start个char大小的char*类型的临时空间
		strcpy(HC[i], &cd[start]);		//cd里记录的编码给HC的第i个数组
	}
	free(cd);				//释放临时空间
}

​

 哈夫曼树的解码

void HuffmanDecoding(HFTree* T,int n,char* pwd,int Len)
{
	//从根结点出发,是走0左子树,1走右子树,直到遇到叶子结点,然后再从根结点出发
	printf("Original:");
	int len=strlen(pwd);	//获取用户输入编码的长度
	int i=0;
	int t=Len;		//初始化为从根结点出发
	while(i<len)
	{
		if(pwd[i]=='0')	//是0,走左子树
		{
			t=T->data[t].lchild;
			i++;
			if(T->data[t].lchild==-1&&T->data[t].rchild==-1)
			{
				printf("%c",T->data[t].data);
				t=Len;		//重新从根结点出发
			}
		}
		if(pwd[i]=='1')	//是1,走右子树
		{
			t=T->data[t].rchild;
			i++;
			if(T->data[t].lchild==-1&&T->data[t].rchild==-1)
			{
				printf("%c",T->data[t].data);
				t=Len;
			}
		}
	}
}

 主函数

int main()
{
	int weight[]={1,2,3,4};
	int len=sizeof(weight)/sizeof(weight[0]);	//求出权值数组长度;
	printf("请输入结点上的字符\n");
	HFTree* T=initTree(weight,len);
	huffmanCode HC;
	createHFTree(T);
	createHuffmanCode(T,HC,len);
	printf("生成的HuffmanTree\n");
	preOrder(T,T->length-1);
	printf("\n生成的HuffmanTreeCode\n");
	for(int i=0;i<len;i++)
	{
		printf("%c:\t",T->data[i].data);
		printf("%s\n",HC[i]);
	}
	
	char s[MAXSIZE];
	printf("请输入01编码\n");
	scanf("%s",s);
	HuffmanDecoding(T,len,s,T->length-1);
	return 0;
}

 完整代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXSIZE 10000

//创建一个树结点
typedef struct TreeNode{
	char data;
	int weight;
	int parent;
	int lchild;
	int rchild;
}TreeNode;

//创建一个树(顺序存储)
typedef struct HFTree{
	TreeNode* data;
	int length;
}HFTree; 

typedef char** huffmanCode;	
/*第一个*是代表它是指针变量,说明它是数组;
第二个*说明它是指针数组
代表这个char类型数组里每个元素都是*huffmanCode变量*/
							
//初始化哈夫曼树
HFTree* initTree(int* weight,int length)
{
	HFTree* T=(HFTree*)malloc(sizeof(HFTree));
	T->data=(TreeNode*)malloc(sizeof(TreeNode)*(2*length-1));
	T->length=length;
	for(int i=0;i<length;i++)
	{
		T->data[i].weight=weight[i];
		T->data[i].parent=0;	//刚开始为空
		T->data[i].lchild=-1;	//刚开始为空
		T->data[i].rchild=-1;	//刚开始为空
	}
	for (int i = 0; i <length; i++)
	{
		//scanf("%c ",&HT[i].data);
		char a = getchar();
		if(a == '\n')												//遇到回车就结束
			break;
		else
			T->data[i].data = a;		//给每个结点赋予数据
	}
	return T;
}

//选择权重最小的函数
int* selectMin(HFTree* T)
{
	int min=10000;
	int secondmin=100000;
	int minIndex,secondminIndex;	//便于最小的两个权值无法在进行对比
	//找出最小的权值
	for(int i=0;i<T->length;i++)	
	{
		if(T->data[i].parent==0)
		{
			if(T->data[i].weight<min)
			{
				min=T->data[i].weight;
				minIndex=i; 	//便于后面求出第二小的权值
			}
		}
	}
	//求出第二小的权值
	for(int i=0;i<T->length;i++)
	{
		if(T->data[i].parent==0&&i!=minIndex)
		{
			if(T->data[i].weight<secondmin)
			{
				secondmin=T->data[i].weight;
				secondminIndex=i;
			}
		}
	}
	int* res=(int*)malloc(sizeof(int)*2); 	//放回两个值要用指针;
	res[0]=minIndex;
	res[1]=secondminIndex;
	return res;
}

//创建哈夫曼树
void createHFTree(HFTree* T)
{
	int* res;
	int min;
	int secondmin;
	int len=T->length*2-1;	//总共有2n-1个结点
	for(int i=T->length;i<len;i++)
	{
		res=selectMin(T);
		min=res[0];
		secondmin=res[1];
		T->data[i].weight=T->data[min].weight+T->data[secondmin].weight;//双亲结点权重等于左右孩子权重之和
		T->data[i].lchild=min;
		T->data[i].rchild=secondmin;
		T->data[min].parent=i;
		T->data[secondmin].parent=i;
		T->length++;
		T->data[i].parent=0;
	}
}

//前序遍历哈夫曼树
void preOrder(HFTree* T,int index)	//index为data数组中的索引
{
	if(index!=-1)
	{
		printf("%d ",T->data[index].weight);
		preOrder(T,T->data[index].lchild);
		preOrder(T,T->data[index].rchild);
	}
}

void createHuffmanCode(HFTree* T, huffmanCode& HC, int n)
{
	HC = (huffmanCode)malloc(sizeof(huffmanCode) * n + 1);		
	/*申请len + 1个huffmanCode大小huffmanCode类型的临时空间
	因为下标是从一开始,所以我们要申请比结点多一个的结点,
	和哈夫曼树的结构对应,方便输出*/
	char* cd = (char*)malloc(sizeof(char) * n);		//申请n个char大小char类型的临时空间,这个临时数组记录每次遍历出来的编码
	int start = 0,c = 0,f = 0;				//start为cd数组记录下标,c初始为叶子结点下标,而后就是孩子结点的下标,f记录双亲结点的下标
	cd[n - 1] = '\0';					//这个就是给printf留着的,因为printf不会生成'\0',如果用puts就不用这句语句了
	for (int i = 0; i <n; i++)		//只要叶子结点的编码
	{
		start = n - 1;					//这句要赋值n的话,start--要写在判断后方
		c = i;							//指向叶子结点
		f = T->data[c].parent;			//指向双亲结点
		while (f != 0)					//根结点没有双亲
		{
			start--;
			if (T->data[f].lchild == c)			//是左孩子就是0,右孩子就为1
				cd[start] = '0';
			else
				cd[start] = '1';
			c = f; 
			f = T->data[c].parent;				//向根结点接近
		}
		HC[i] = (char*)malloc(sizeof(char) * (n - start));	//给数组里的数组申请n - start个char大小的char*类型的临时空间
		strcpy(HC[i], &cd[start]);		//cd里记录的编码给HC的第i个数组
	}
	free(cd);				//释放临时空间
}

void HuffmanDecoding(HFTree* T,int n,char* pwd,int Len)
{
	//从根结点出发,是走0左子树,1走右子树,直到遇到叶子结点,然后再从根结点出发
	printf("Original:");
	int len=strlen(pwd);	//获取用户输入编码的长度
	int i=0;
	int t=Len;		//初始化为从根结点出发
	while(i<len)
	{
		if(pwd[i]=='0')	//是0,走左子树
		{
			t=T->data[t].lchild;
			i++;
			if(T->data[t].lchild==-1&&T->data[t].rchild==-1)
			{
				printf("%c",T->data[t].data);
				t=Len;		//重新从根结点出发
			}
		}
		if(pwd[i]=='1')	//是1,走右子树
		{
			t=T->data[t].rchild;
			i++;
			if(T->data[t].lchild==-1&&T->data[t].rchild==-1)
			{
				printf("%c",T->data[t].data);
				t=Len;
			}
		}
	}
}
int main()
{
	int weight[]={1,2,3,4};
	int len=sizeof(weight)/sizeof(weight[0]);	//求出权值数组长度;
	printf("请输入结点上的字符\n");
	HFTree* T=initTree(weight,len);
	huffmanCode HC;
	createHFTree(T);
	createHuffmanCode(T,HC,len);
	printf("生成的HuffmanTree\n");
	preOrder(T,T->length-1);
	printf("\n生成的HuffmanTreeCode\n");
	for(int i=0;i<len;i++)
	{
		printf("%c:\t",T->data[i].data);
		printf("%s\n",HC[i]);
	}
	
	char s[MAXSIZE];
	printf("请输入01编码\n");
	scanf("%s",s);
	HuffmanDecoding(T,len,s,T->length-1);
	return 0;
}

 编译的结果

 

猜你喜欢

转载自blog.csdn.net/Lookdrama/article/details/127452822
今日推荐