数据结构——二叉树的基本操作(三)

一、实验目的及要求

1.理解二叉树的基本概念和特点
2.掌握二叉树的链式存储结构
3.掌握二叉树的基本操作
4.掌握二叉树遍历操作
5.掌握哈夫曼树的构造算法和基本操作

二、实验内容(或实验原理、实验拓扑)

  1. 假定用于通信的电文仅由a,b,c,d,e,f,6个字母组成,各字母在电文中出现的频率分别为:2,3,4,7,8,9。试构造一棵哈夫曼树,并输出对应的哈夫曼编码和WPL带权路径长度。
    具体效果如下:
    数据结构——二叉树的基本操作(三)_第1张图片
    2.设计一个程序,构造一棵哈夫曼树,输出对应的哈夫曼编码和WPL带权路径长度,并用如下表所示的数据进行验证。
    单词 The of a to and in that he is at on for His are be
    频率 1192 677 541 518 462 450 242 195 190 181 174 157 138 124 123
    具体效果如下:
    数据结构——二叉树的基本操作(三)_第2张图片

三、实验设计方案(包括实验步骤、设计思想、算法描述或开发流程等)

(一)第一题算法的基本操作函数以及主函数(详细代码见附录):
(1) 构造哈夫曼树CreateHT(HTNode ht[],int n)
(2) 根据哈夫曼树求哈夫曼编码CreateHCode(HTNode ht[],HCode hcd[],int n)
(3) 输出哈夫曼编码DispHCode(HTNode ht[],HCode hcd[],int n)
(4) 主函数main():根据问题依次调用基本操作函数并编写通俗易懂的语句输出。
(二)第二题算法的基本操作函数以及主函数(详细代码见附录):
(1) 构造哈夫曼树CreateHT(HTNode ht[],int n)
(2) 根据哈夫曼树求对应的哈夫曼编码CreateHCode(HTNode ht[],HCode hcd[],int n)
(3) 输出哈夫曼编码DispHCode(HTNode ht[],HCode hcd[],int n)
(4)主函数main():根据问题依次调用基本操作函数并编写通俗易懂的语句输出。

四、实验结果(包括设计效果、测试数据、运行结果等)

(一)运行结果如下:
数据结构——二叉树的基本操作(三)_第3张图片
(二)运行结果如下:
数据结构——二叉树的基本操作(三)_第4张图片

五、实验小结(包括收获、心得体会、注意事项、存在问题及解决办法、建议等)

这次实验通过哈夫曼树构造过程可知,n 个权值构造哈夫曼树需n-1次合并,每次合并,森林中的树数目减1,最后森林中只剩下一棵树,即为所求的哈夫曼树。另外一棵有n个叶子结点的Huffman树有2n-1个结点。

六、附录(包括作品、流程图、源程序及命令清单等)

(一):
#include 
#include 
#define N 50		//叶子结点数
#define M 2*N-1		//树中结点总数
typedef struct
{
	char data[5];	//结点值
	int weight;	//权重
	int parent;		//双亲结点
	int lchild;		//左孩子结点
	int rchild;		//右孩子结点
} HTNode;
typedef struct
{
	char cd[N];		//存放哈夫曼码
	int start;
} HCode;
void CreateHT(HTNode ht[],int n)
{
	int i,k,lnode,rnode;
	double min1,min2;
	for (i=0;i<2*n-1;i++)			//所有结点的相关域置初值-1
    {
       ht[i].parent=ht[i].lchild=ht[i].rchild=-1; 
    }
	for (i=n;i<2*n-1;i++)			//构造哈夫曼树
	{
		min1=min2=32767;			//lnode和rnode为最小权重的两个结点位置
		lnode=rnode=-1;
		for (k=0;k<=i-1;k++)
			if (ht[k].parent==-1)	//只在尚未构造二叉树的结点中查找
			{
				if (ht[k].weight<min1)
				{
					min2=min1;
					rnode=lnode;
					min1=ht[k].weight;
					lnode=k;
				}
				else 
				{
                  if (ht[k].weight<min2)
				  {min2=ht[k].weight;
				    rnode=k;}
				}		
			}
		ht[i].weight=ht[lnode].weight+ht[rnode].weight;
		ht[i].lchild=lnode;ht[i].rchild=rnode;
		ht[lnode].parent=i;ht[rnode].parent=i;
	}
}
void CreateHCode(HTNode ht[],HCode hcd[],int n)
{
	int i,f,c;
	HCode hc;
	for (i=0;i<n;i++)	//根据哈夫曼树求哈夫曼编码
	{
		hc.start=n;c=i;
		f=ht[i].parent;
		while (f!=-1)	//循序直到树根结点
		{
			if (ht[f].lchild==c)	//处理左孩子结点
				hc.cd[hc.start--]='0';
			else					//处理右孩子结点
				hc.cd[hc.start--]='1';
			c=f;f=ht[f].parent;
		}
		hc.start++;		//start指向哈夫曼编码最开始字符
		hcd[i]=hc;
	}
}
void DispHCode(HTNode ht[],HCode hcd[],int n)
{
	int i,k;
	int sum=0,m=0;
	int j;
	printf("  输出哈夫曼编码:\n"); //输出哈夫曼编码
	for (i=0;i<n;i++)
	{
		j=0;
		printf("      %s:\t",ht[i].data);
		for (k=hcd[i].start;k<=n;k++)
		{
			printf("%c",hcd[i].cd[k]);
			j++;
		}
		m+=ht[i].weight;
		sum+=ht[i].weight*j;
		printf("\n");
	}
	printf("\n  带权路径长度 WPL=%d\n",sum);
}
int main()
{
	int n=6,i;		//n表示初始字符串的个数
	char *str[]={"a","b","c","d","e","f"};
	int fnum[]={2,3,4,7,8,9};
	HTNode ht[M];
	HCode hcd[N];
	for (i=0;i<n;i++)
	{
		strcpy(ht[i].data,str[i]);
		ht[i].weight=fnum[i];
	}
	printf("\n");
	CreateHT(ht,n);
	CreateHCode(ht,hcd,n);
	DispHCode(ht,hcd,n);
	printf("\n");
}
 
(二):
#include 
#include 
#define N 50		//叶子节点数
#define M 2*N-1		//树中节点总数
typedef struct
{
	char data[5];	//节点值
	int weight;		//权重
	int parent;		//双亲节点
	int lchild;		//左孩子节点
	int rchild;		//右孩子节点
} HTNode;
typedef struct
{
	char cd[N];		//存放哈夫曼码
	int start;
} HCode;
void CreateHT(HTNode ht[],int n) //构造哈夫曼树算法
{
	int i,k,lnode,rnode;
	int min1,min2;
	for (i=0;i<2*n-1;i++)			//所有节点的相关域置初值-1
		ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
	for (i=n;i<2*n-1;i++)			//构造哈夫曼树
	{
		min1=min2=32767;			//lnode和rnode为最小权重的两个节点位置
		lnode=rnode=-1;
		for (k=0;k<=i-1;k++)
			if (ht[k].parent==-1)	//只在尚未构造二叉树的节点中查找
			{
				if (ht[k].weight<min1)
				{
					min2=min1;rnode=lnode;
					min1=ht[k].weight;lnode=k;
				}
				else if (ht[k].weight<min2)
				{
					min2=ht[k].weight;rnode=k;
				}
			}
		ht[lnode].parent=i;ht[rnode].parent=i;
		ht[i].weight=ht[lnode].weight+ht[rnode].weight;
		ht[i].lchild=lnode;ht[i].rchild=rnode;
	}
}
void CreateHCode(HTNode ht[],HCode hcd[],int n)//根据哈夫曼树求对应的哈夫曼编码的算法
{
	int i,f,c;
	HCode hc;
	for (i=0;i<n;i++)	//根据哈夫曼树求哈夫曼编码
	{
		hc.start=n;c=i;
		f=ht[i].parent;
		while (f!=-1)	//循序直到树根节点
		{
			if (ht[f].lchild==c)	//处理左孩子节点
				hc.cd[hc.start--]='0';
			else					//处理右孩子节点
				hc.cd[hc.start--]='1';
			c=f;f=ht[f].parent;
		}
		hc.start++;		//start指向哈夫曼编码最开始字符
		hcd[i]=hc;
	}
}
void DispHCode(HTNode ht[],HCode hcd[],int n) //输出哈夫曼编码
{
	int i,k;
	int sum=0,m=0,j;
	printf("输出哈夫曼编码:\n"); //输出哈夫曼编码
	for (i=0;i<n;i++)
	{
		j=0;
		printf("      %s:\t",ht[i].data);
		for (k=hcd[i].start;k<=n;k++)
		{
			printf("%c",hcd[i].cd[k]);
			j++;
		}
		m+=ht[i].weight;
		sum+=ht[i].weight*j;
		printf("\n");
	}
	printf("带权路径长度WPL=%d\n",sum);
}
int main()
{
	int n=15,i;
	char *str[]={"The","of","a","to","and","in","that","he","is","at","on","for","His","are","be"};
	int fnum[]={1192,677,541,518,462,450,242,195,190,181,174,157,138,124,123};
	HTNode ht[M];
	HCode hcd[N];
	for (i=0;i<n;i++)
	{
		strcpy(ht[i].data,str[i]);
		ht[i].weight=fnum[i];
	}
	CreateHT(ht,n);
	CreateHCode(ht,hcd,n);
	DispHCode(ht,hcd,n);
}

你可能感兴趣的:(学习笔记)