【C语言】哈夫曼树建立与编码

原理:

严版教材p144,真题册P163

算法步骤:

(1) 赫夫曼树的创建

1)定义赫夫曼树的存储结构,包含指向双亲结点、左右孩子结点的指针、和权值信息;

2)定义一个返回HTNode*类型的函数,用来创建赫夫曼树,闯入的参数为叶子结点n,和权值数组w;

3)根据叶子结点个数确定赫夫曼树结点的个数m,然后定义一个结点指针数组HTNode* HT,作为赫夫曼树;

4)将赫夫曼树初始化,叶结点权值为权值数组中对应的权值,非叶子结点权值为0,所结点的双亲结点和孩子结点指针都为0,此时可以将每个结点看作一棵独立的树;

5)构建赫夫曼树,填充非叶子节点。做一个循环,从n+1到m,遍历每一个非叶子节点:

对于一个非叶子节点i(此时因为还没有填充该结点,应该算作一棵独立的树),从还未并入赫夫曼树的树中选择两棵头结点权值最小的树(假设它们在数组中的下标为s1、s2)

令i的左孩子指向s1,右孩子指向s2,权值为s1和s2两个结点的权值之和

再删除这两个结点,使它们的双亲结点指针为1;

6)循环结束,返回HTNode*类型的指针变量HT。

补充:

关于选择权值最小的树的算法:

1)构造一个空值函数,传入的参数为赫夫曼树结点指针数组HT,数组大小n(范围为1到i-1,有权值的结点),和两个int*的指针变量s1、s2;

2)定义一个min变量,用来寻找最小权值的数组元素下标,给min赋初值。做一个循环先找到第一个最小权值的数组元素,这个结点的双亲结点为0表示还没有并入赫夫曼树;

3)打擂台法确定最小值。做一个循环,从第一个元素遍历到最后一个元素,如果该元素双亲结点为0且权值比当前最小值结点权值要小就将该元素下标赋值给min。循环结束将min赋值给*s1;

4)确定第二个变量*s2的值。与第二、三步相同,限制条件为选中的结点下标不能等于*s2。

(2)赫夫曼编码以及计算wpl

1)构造一个返回char**的函数,传入的参数为赫夫曼树即HTNode HT[],以及叶结点的个数n和带权路径长度变量int*wpl;

2)创建一个存储赫夫曼树编码串的数组,大小为n+1,代表在1到n的位置存储n个字符串,类型为char**;

3)创建一个临时字符串数组,大小为n,用来保存单个字符串编码,并在末尾设置结束符‘\0’;

4)下面做两个循环,第一个循环用来遍历每一个叶结点:

取一个叶结点,设置father变量指向双亲结点下标,current变量指向当前结点下标,变量start标记临时数组法当前字符的下,每次循环默认为n-1,level计算路径长度,默认为0;

5)第二个循环,从叶结点回溯至根节点,求取路径,判断双亲结点指针father是否为0,等于0时结束循环:

判断当前结点是双亲结点的左孩子还是右孩子,如果为左孩子则临时数组赋值'0',否则为1;

将father赋值给current,current的双亲结点下标赋值给father,同时level自加1;

6)继续第一个循环,用2)创建的指针数组中取一个元素存储创建一个大小为n-start的字符串数组的地址,然后将临时数组中start开始的字符赋值给这个刚刚创建的字符串数组,再用level计算带权路径长度*wpl。

7)第一个循环结束后,将临时数组空间释放掉,返回指针数组的数组名。

改编代码(纯C):

#include 
#include 
#include 

typedef struct
{
	int parent;
	int lchild;
	int rchild;
	int weight;
}HTNode, *HuffmanTree;
typedef char** HuffmanCode;


void Select(HuffmanTree HT, int n, int* s1, int* s2)
{
	int i, min;//min用来临时存储最小值节点的下标

	//给min赋初值
	for (i = 1; i <= n; ++i)
	{
		if (HT[i].parent == 0)
		{
			min = i;
			break;
		}
	}

	//寻找第一个最小值结点
	for (i = 1; i <= n; ++i)
	{
		if (HT[i].parent == 0)
			if (HT[i].weight < HT[min].weight)
				min = i;
	}

	*s1 = min;

	//给min赋初值,且与s1不同
	for (i = 1; i <= n; ++i)
	{
		if (HT[i].parent == 0 && i != *s1)
		{
			min = i;
			break;
		}
	}

	//寻找第二个最小值结点
	for (i = 1; i <= n; ++i)
	{
		if (HT[i].parent == 0 && i != *s1)
			if (HT[i].weight < HT[min].weight)
				min = i;
	}
	*s2 = min;

}

HuffmanTree CreateHuff(int n, int* w)
{
	int i, m = 2 * n - 1;//叶子结点为n的哈夫曼树有m个结点
	HuffmanTree HT = (HTNode*)malloc((m + 1) * sizeof(HTNode));//0号单元未用
	//叶子结点初始化
	for (i = 1; i <= n; ++i, ++w)
	{
		HT[i].weight = *w;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].parent = 0;
	}

	//非叶子结点初始化
	for (i = n + 1; i <= m; ++i)
	{
		HT[i].weight = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].parent = 0;
	}

	//建哈夫曼树
	for (i = n + 1; i <= m; ++i)
	{
		int s1, s2;

		//选择两棵根结点权值最小的树作为新结点的左右子树
		Select(HT, i - 1, &s1, &s2);

		//对新节点进行赋值
		HT[i].lchild = s1;
		HT[i].rchild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight;


		//删除刚才选出的两个节点
		HT[s1].parent = i;
		HT[s2].parent = i;
	}
	return HT;
}

//哈夫曼编码并计算wpl
HuffmanCode HuffmanCoding(HTNode HT[], int n, int* wpl)
{
	HuffmanCode HC = (char**)malloc((n + 1) * sizeof(char*));//分配n个指向哈夫曼编码串的指针,0号单元未用
	char* cd = (char*)malloc(n * sizeof(char));//创建一个临时字符串数组,存储单个叶结点的哈夫曼编码串,正则二叉树的最大高度是n
	cd[n - 1] = '\0';//字符数组最后一个位置设置结束符
	*wpl = 0;//赋初值

	int i, start;
	int current;//标记当前结点
	int father;//标记当前结点的父结点
	int level;//用来存储路径长度

	//逐字符求哈夫曼编码
	for (i = 1; i <= n; i++)
	{
		start = n - 1;//编码结束符位置,start必须在循环内
		current = i;
		father = HT[current].parent;
		level = 0;

		//从叶子节点到根逆向求编码
		while (father != 0)
		{
			if (HT[father].lchild == current)//注意这里写错过,写成了i
				cd[--start] = '0';
			else
				cd[--start] = '1';
			current = father;
			father = HT[current].parent;
			++level;
		}

		HC[i] = (char*)malloc((n - start) * sizeof(char));/*简记:结束符的下标为n-1,从结束符到下标为start的字符个数为n-1 - start + 1,即n-start*/
		strcpy(HC[i], &cd[start]);
		*wpl += level * HT[i].weight;
	}

	free(cd);//用完以后不要忘记释放工作空间
	return HC;
}

int main()
{
	int i, n, wpl;
	printf("Please input n:");
	scanf("%d", &n);
	int* w = (int*)malloc(n * sizeof(int));
	for (i = 0; i < n; i++)
	{
		printf("Please input %dth weight:", i+1);
		scanf("%d", &w[i]);
	}
	HuffmanTree HT = CreateHuff(n, w);

	HuffmanCode HC = HuffmanCoding(HT, n, &wpl);

	//输出树的带权路径长度
	printf("WPL=%d\n", wpl);

	//输出编码
	for (i = 1; i <= n; ++i)
	{
		printf("%dth:%s\n", i, HC[i]);
	}
	system("pause");
}

 

你可能感兴趣的:(#,初试数据结构学习,#,C考研真题解析)