哈夫曼树以及哈夫曼编码的相关知识

首先定义哈夫曼树结构体:

typedef struct HTNode{
	char ch;
	int weight;
	int parent,lchild,rchild;
	int tag;//是否访问过的标志 
	char code[56];//哈夫曼码 
	int ceng;//表示层数 
}HTNode,*HuffmanTree; 

我们前26个数组存储小写字母,27-52存储大写字母,53-54存储,。空格?四个字符

所以一共有56种字符,那么建立哈夫曼树就要建立55个额外的节点,那么数组就要准备110个

首先准备英文文章,存在记事本里,在读取

1.英文文本:

Today I begin a new lifeToday I shed my old skin which hath, too long, suffered the bruises of failure and the wounds of mediority.Today I am born anew and my birthplace is a vineyard where there is fruit for all.Today I will pluck grapes of wisdom from the tallest and fullest vines in the vineyard,for these were planted by the wisest of my profession who have come before me,generation upon generation.Today I will savor the taste of grapes from these vines and verily I will swallow the seed of success buried in each and new life will sprout within me.The career I have chosen is laden with opportunity yet it is fraught with heartbreak and despair and the bodies of those who have failed, were they piled one atop another, would cast a shadow down upon all the pyramids of the earth.Yet I will not fail, as the others, for in my hands I now hold the charts which will guide through perilous waters to shores which only yesterday seemed but a dream.Failure no longer will be my payment for strug

文章的读取

int readFile(const char *fname, char *buffer, int bufferLen)//读取文件 
{
	int retLen=0;
	FILE *f;
	f = fopen(fname, "r"); //r=打开一个已有的文本文件,允许读取文件。
	if(f){
		retLen= fread(buffer, 1, bufferLen, f);
		buffer[retLen]=0;
		fclose(f);
	}
	if(retLen <= 0){ 
		printf("读取文件错误,或者空文件!");
		return 0;
	}
	return retLen;
}

先对各个成员初始化左右孩子下标初始化为-1,权重初始化为0,字符ch存储相应字符,权重weight初始化为0,哈夫曼码部分暂时存储'\0';

tag表示是否被访问过,后面的部分函数会用到;

统计文章中各个字符数:

int tongji(HTNode *a,char *b)//统计一篇英文文章各字符出现的个数 
{
	int j=0;
	for(int i=0;i<26;i++)
	f	a[i].ch='a'+i;
	or(int i=26;i<52;i++)
		a[i].ch='A'+j++;
		
	a[52].ch=',';
	a[53].ch='.';
	a[54].ch=' ';
	a[55].ch='?'; 
	for(int i=0;i<111;i++) //初始化
	{
		a[i].parent=0;
		a[i].lchild=-1;
		a[i].rchild=-1; 
		a[i].weight=0;
		a[i].tag=0;
		a[i].code[0]='\0';
		a[i].ceng=0;
	}
	for(int i=0;i<1000;i++)
		for(int j=0;j<56;j++)
			if(b[i]==a[j].ch)
				(a[j].weight)++;
}

返回权值最小节点下标函数(为后面创建哈夫曼树做准备):

int MinNode(HTNode *a)//返回权值最小节点的下标 
{
	int t;//储存权值最小节点的下标  
	for(int i=0;i<56+s;i++)
		if(a[i].tag!=1)
			t=i;
	for(int i=0;i<56+s;i++)
		if(a[i].tag!=1&&a[i].weight<a[t].weight)
			t=i;
	a[t].tag=1;
	return t;
}

创建哈夫曼树(共56种字符,所以要在for循环种循环55次)‘#’表示建立哈夫曼树中生成的结点:

int CreatHfmTree(HTNode *a)//创建哈夫曼数 
{
	int i=56;
	for(int j=0;j<55;j++)//一共需要调用55次构建哈夫曼树函数 
	{	
		int min1,min2;
		min1=MinNode(a);
		min2=MinNode(a);
		a[i+j].ch='#';
		a[i+j].weight=(a[min1].weight+a[min2].weight);
		a[i+j].lchild=min1;
		a[i+j].rchild=min2;	
		a[min1].parent=i+j;
		a[min2].parent=i+j;
		s++;
	}
}

建立好哈夫曼树后,进行哈夫曼编码。从从根向叶子节点遍历比较困难,所以我们从叶子节点向根寻找,把他暂时存储到temp字符数组中。

再依次把temp字符数组中的字符串逆序存储到哈夫曼数结构体中的code即可再依次把temp字符数组中的字符串逆序存储到哈夫曼数结构体中的code即可

int HfmCode(HTNode *a)//编码 
{
	int i=0,j=0;
	int next,now;//now记录当前节点的下标。next记录parent节点的下标 
	char temp[56];
	HTNode *p; 
	for(i=0;i<56;i++)
	{
		j=0;
		p=&a[i];
		next=p->parent;
		now=i;
		while(now!=110)
		{
			if(a[next].lchild==now)
				temp[j++]='0';
			else  
				temp[j++]='1';
			now=next;
			next=a[next].parent; 
		}
		int g=j-1;
		int k;
		for(k=0;k<j;k++)
		{
			a[i].code[k]=temp[g];
			g--;
		}
		a[i].code[k]='\0';
	}
}

这时候我们在主函数中通过for循环依次输出数组下标,存储字符,权重,双亲节点下标,左右孩子节点下标,以及哈夫曼码:我这里手动算了一下全部节点的和为1000;说明算对了

最后就是把文章翻译成哈夫曼编码(for循环一一对应就好,一个一个来,我这里没考虑时间复杂度,我这个函数的时间复杂度度应该非常高):

int CoutCode(HTNode *a,char p[1000])//显码 
{
	char *q=p;
	while(*q!='\0')
	{
		for(int i=0;i<56;i++)
			if(a[i].ch==*q)
				printf("%s",a[i].code);
		q++;
	}
}

在主函数中验证打印

哈哈有点看不清是不是

这里我实际做了一下验证

第一个单词Today的哈夫曼码应该为:

0000101101011000100100000

实际为:

0000101101011000100100000

运行正确

下面就是全部代码了

我这里还多了一个打印树形的部分,刚接触数据结构,想了好几天一没想到好的办法,所以我就用层次遍历看了一下

我就手动把前八层树动输出了。。。。。。。后面的28层用了一个for循环实现了(ps:有会的可以教下我顺序树的显示树形,这个下标套来套去我就晕了。。。)

层次遍历

int rechar=130;//后28行处理 
	for(int i=15;i<64;i=i+2)
	{
		for(int j=rechar-1;j<rechar+2;j++)
			ch[i+1][j]='-';
		ch[i+2][rechar-1]=a[child[c++]].ch;
		ch[i+2][rechar+1]=a[child[c++]].ch;
		rechar--;
	}

 好了下面给出全部代码

#include<stdio.h>
#include<iostream>
#include<queue>
using namespace std;

typedef struct HTNode{
	char ch;
	int weight;
	int parent,lchild,rchild;
	int tag;//是否访问过的标志 
	char code[56];//哈夫曼码 
	int ceng;//表示层数 
}HTNode,*HuffmanTree; 

int tongji(HTNode *a,char *b)//统计一篇英文文章各字符出现的个数 
{
	int j=0;
	for(int i=0;i<26;i++)
		a[i].ch='a'+i;
	for(int i=26;i<52;i++)
		a[i].ch='A'+j++;
		
	a[52].ch=',';
	a[53].ch='.';
	a[54].ch=' ';
	a[55].ch='?'; 
	for(int i=0;i<111;i++) {
		a[i].parent=0;
		a[i].lchild=-1;
		a[i].rchild=-1; 
		a[i].weight=0;
		a[i].tag=0;
		a[i].code[0]='\0';
		a[i].ceng=0;
	}
	for(int i=0;i<1000;i++)
		for(int j=0;j<56;j++)
			if(b[i]==a[j].ch)
				(a[j].weight)++;
}

int s=0;

int MinNode(HTNode *a)//返回权值最小节点的下标 
{
	int t;//储存权值最小节点的下标  
	for(int i=0;i<56+s;i++)
		if(a[i].tag!=1)
			t=i;
	for(int i=0;i<56+s;i++)
		if(a[i].tag!=1&&a[i].weight<a[t].weight)
			t=i;
	a[t].tag=1;
	return t;
}

int CreatHfmTree(HTNode *a)//创建哈夫曼数 
{
	int i=56;
	for(int j=0;j<55;j++)//一共需要调用55次构建哈夫曼树函数 
	{	
		int min1,min2;
		min1=MinNode(a);
		min2=MinNode(a);
		a[i+j].ch='#';
		a[i+j].weight=(a[min1].weight+a[min2].weight);
		a[i+j].lchild=min1;
		a[i+j].rchild=min2;	
		a[min1].parent=i+j;
		a[min2].parent=i+j;
		s++;
	}
}

int HfmCode(HTNode *a)//编码 
{
	int i=0,j=0;
	int next,now;//now记录当前节点的下标。next记录parent节点的下标 
	char temp[56];
	HTNode *p; 
	for(i=0;i<56;i++)
	{
		j=0;
		p=&a[i];
		next=p->parent;
		now=i;
		while(now!=110)
		{
			if(a[next].lchild==now)
				temp[j++]='0';
			else  
				temp[j++]='1';
			now=next;
			next=a[next].parent; 
		}
		int g=j-1;
		int k;
		for(k=0;k<j;k++)
		{
			a[i].code[k]=temp[g];
			g--;
		}
		a[i].code[k]='\0';
	}
}

int CoutCode(HTNode *a,char p[1000])//显码 
{
	char *q=p;
	while(*q!='\0')
	{
		for(int i=0;i<56;i++)
			if(a[i].ch==*q)
				printf("%s",a[i].code);
		q++;
	}
}

int addceng(HTNode *a)//对每一个节点的层数赋值 
{
	a[110].ceng=1;
	int i=1,j;
	int p;
	for(i;i<50;i++)
		for(j=109;j>=0;j--)
		{
			p=a[j].parent;
			if(a[p].ceng==i)
			{
				a[j].ceng=i+1;
				a[j].tag=1;
			}
		}
}

int child[110]={110,108,109,104,105,106,107,78,4,99,100,101,102,103,54,93,18,17,7,8,19,94,0,14,95,96,97,24,89,5,22,90,91,3,11,92,13,85,2,86,12,15,87,20,88,84,45,53,52,21,34,1,6,83,10,82,50,81,31,80,51,79,49,78,48,77,47,76,46,75,44,74,43,73,42,72,41,71,40,70,39,69,38,68,37,67,36,66,35,65,33,64,32,63,30,62,29,61,28,60,27,59,26,58,25,57,23,56,16};
int path[110]={70,131,55,86,115,146,48,63,78,93,108,123,138,153,45,52,75,82,89,96,105,112,119,126,135,142,44,47,104,107,123,128,134,137,140,143,45,48,122,125,126,129,139,142};
int roof[110]={100,70,130,55,85,115,145,48,62,78,92,108,122,138,152,45,51,75,81,89,95,105,111,119,44,46,104,106,123,127,134,45,47,122,124,126,128};

int leverorder(HTNode *a)//层次遍历 (并且在左边的放在左边输出,在右边的放在右边输出,按照次序) 
{
	int i=1,j,k;
	for(i;i<40;i++)
	{
		for(int l=0;l<120-3*i;l++)
			printf(" ");
		int num=1;
		for(j=0;j<111;j++)
			for(j=0;j<111;j++)
			{
				if(a[j].ceng==i)
					printf("%c    ",a[j].ch);
			}
		num++; 
		for(k=0;k<5-2*i;k++)
			printf("    ");
		cout<<endl;
	}
}

void CoutHTNode(HTNode *a)//输出树形 
{
	char ch[64][201];
	for(int i=0;i<64;i++)
		for(int j=0;j<201;j++)
			ch[i][j]=' ';
	int c=0;//c,使用chlid中第几个数。
	int p=0,r=0;//path数字的行号(“-----”),roof的行号 
	
	ch[0][100]=a[child[c++]].ch;//前8行处理,手动打印.... 
	for(int i=70;i<131;i++)ch[1][i]='-';
	ch[2][70]=a[child[c++]].ch;ch[2][130]=a[child[c++]].ch;
	for(int i=55;i<86;i++)ch[3][i]='-';for(int i=115;i<146;i++)ch[3][i]='-';
	ch[4][55]=a[child[c++]].ch;ch[4][85]=a[child[c++]].ch;ch[4][115]=a[child[c++]].ch;ch[4][145]=a[child[c++]].ch;
	for(int i=48;i<63;i++)ch[5][i]='-';for(int i=78;i<93;i++)ch[5][i]='-';
	for(int i=108;i<123;i++)ch[5][i]='-';for(int i=138;i<153;i++)ch[5][i]='-';
	ch[6][48]=a[child[c++]].ch;ch[6][62]=a[child[c++]].ch;ch[6][78]=a[child[c++]].ch;ch[6][92]=a[child[c++]].ch;
	ch[6][108]=a[child[c++]].ch;ch[6][122]=a[child[c++]].ch;ch[6][138]=a[child[c++]].ch;ch[6][152]=a[child[c++]].ch;
	for(int i=45;i<52;i++)ch[7][i]='-';for(int i=75;i<82;i++)ch[7][i]='-';for(int i=89;i<96;i++)ch[7][i]='-';
	for(int i=105;i<112;i++)ch[7][i]='-';for(int i=119;i<126;i++)ch[7][i]='-';for(int i=135;i<142;i++)ch[7][i]='-';
	ch[8][45]=a[child[c++]].ch;ch[8][51]=a[child[c++]].ch;ch[8][75]=a[child[c++]].ch;ch[8][81]=a[child[c++]].ch;ch[8][89]=a[child[c++]].ch;
	ch[8][95]=a[child[c++]].ch;ch[8][105]=a[child[c++]].ch;ch[8][111]=a[child[c++]].ch;ch[8][119]=a[child[c++]].ch;
	ch[8][125]=a[child[c++]].ch;ch[8][135]=a[child[c++]].ch;ch[8][141]=a[child[c++]].ch;
	for(int i=44;i<47;i++)ch[9][i]='-';for(int i=104;i<107;i++)ch[9][i]='-';for(int i=123;i<128;i++)ch[9][i]='-';
	for(int i=134;i<137;i++)ch[9][i]='-';for(int i=140;i<143;i++)ch[9][i]='-';
	ch[10][44]=a[child[c++]].ch;ch[10][46]=a[child[c++]].ch;ch[10][104]=a[child[c++]].ch;ch[10][106]=a[child[c++]].ch;
	ch[10][123]=a[child[c++]].ch;ch[10][127]=a[child[c++]].ch;ch[10][134]=a[child[c++]].ch;
	ch[10][136]=a[child[c++]].ch;ch[10][140]=a[child[c++]].ch;ch[10][142]=a[child[c++]].ch;
	for(int i=45;i<48;i++)ch[11][i]='-';for(int i=122;i<125;i++)ch[11][i]='-';
	for(int i=126;i<129;i++)ch[11][i]='-';for(int i=139;i<142;i++)ch[11][i]='-';
	ch[12][45]=a[child[c++]].ch;ch[12][47]=a[child[c++]].ch;ch[12][122]=a[child[c++]].ch;
	ch[12][124]=a[child[c++]].ch;ch[12][126]=a[child[c++]].ch;ch[12][128]=a[child[c++]].ch;
	ch[12][139]=a[child[c++]].ch;ch[12][141]=a[child[c++]].ch;
	for(int i=44;i<47;i++)ch[13][i]='-';for(int i=121;i<124;i++)ch[13][i]='-';for(int i=127;i<130;i++)ch[13][i]='-';
	for(int i=140;i<143;i++)ch[13][i]='-';
	ch[14][44]=a[child[c++]].ch;ch[14][46]=a[child[c++]].ch;ch[14][121]=a[child[c++]].ch;
	ch[14][123]=a[child[c++]].ch;ch[14][127]=a[child[c++]].ch;ch[14][129]=a[child[c++]].ch;
	ch[14][140]=a[child[c++]].ch;ch[14][142]=a[child[c++]].ch;
	ch[15][44]='+';
	for(int i=45;i<130;i++)ch[15][i]='-';ch[15][130]='+';
	
	int rechar=130;//后28行处理 
	for(int i=15;i<64;i=i+2)
	{
		for(int j=rechar-1;j<rechar+2;j++)
			ch[i+1][j]='-';
		ch[i+2][rechar-1]=a[child[c++]].ch;
		ch[i+2][rechar+1]=a[child[c++]].ch;
		rechar--;
	}
	
	
	for(int i=0;i<64;i++)
	{
		for(int l=0;l<201;l++)
			printf("%c",ch[i][l]);
		cout<<endl;
	}
}

int readFile(const char *fname, char *buffer, int bufferLen)//读取文件 
{
	int retLen=0;
	FILE *f;
	f = fopen(fname, "r"); //r=打开一个已有的文本文件,允许读取文件。
	if(f){
		retLen= fread(buffer, 1, bufferLen, f);
		buffer[retLen]=0;
		fclose(f);
	}
	if(retLen <= 0){ 
		printf("读取文件错误,或者空文件!");
		return 0;
	}
	return retLen;
}

int main()
{
	char buf[1000];
	const char fname[]= "D:/数据结构cpp文件/徐嘉辉 2020202527 20计科05 第二次实验/yingwen.txt";
	
	int retLen= readFile(fname,buf,1000);
	if(retLen != 0) {
		printf("读取的数据是: %s \n",buf);
	}
	
	cout<<endl;
	printf("建立56个数组,前26储存小写字母,后26储存大写字母,最后4个空间储存,.空格?四个符号\n");
	HTNode num[111];//56+55 n个数据2n-1个节点
	tongji(num,buf);
	cout<<endl;
	CreatHfmTree(num);//建立哈夫曼树 
	
	HfmCode(num);
	cout<<"按照数组下标,储存的字符,权重,双亲,左孩子,右孩子,哈夫曼码的顺序输出"<<endl; 
	cout<<"下标   "<<"字符   "<<"权重 "<<"双亲下标"<<" 左孩子下标 "<<"  右孩子下标"<<"  哈夫曼码"<<endl;
	for(int i=0;i<111;i++)
	{
		int f=0;
		printf("%4d   %4c   %4d   %4d   %10d   %10d  ",i,num[i].ch,num[i].weight,num[i].parent,num[i].lchild,num[i].rchild);
		while(num[i].code[f]!='\0')
			putchar(num[i].code[f++]);
		cout<<endl;	
	}
	
	for(int i=0;i<111;i++)
		num[i].tag=0;
	
	cout<<endl; 
	cout<<"整篇文章的哈夫曼编码为:"<<endl; 
	CoutCode(num,buf); 
	cout<<endl;
	
	addceng(num);
	cout<<endl<<"每个节点的层数为:"<<endl;
	for(int i=0;i<111;i++)
		printf("%d ",num[i].ceng);
		
	cout<<endl<<"层次遍历:"<<endl;
	leverorder(num);
	cout<<endl;
	
	CoutHTNode(num);
}

整个函数的运行结果:

 

 

 

 

 

 欢迎赐教

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值