树12——构造哈夫曼树并输出哈夫曼编码

树12——哈夫曼树

哈夫曼树

为一组权值分别为2、4、7、15的结点序列构造一棵哈夫曼树,然后输出相应的哈夫曼编码。


为了便于设计,可利用一个二维数组实现哈夫曼树的算法。因为需要保存字符的权重、双亲结点位置、左孩子结点位置和右孩子结点的位置。所以需要将数组设计成n行四列。因此,哈夫曼树的类型定义如下:

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

定义一个类型为HuffumanCode的变量HT,用来存放每一个叶子结点的哈夫曼编码。初始时,将每一个叶子结点的双亲结点域、左孩子域和右孩子域初始化为0。如果有n个叶子结点,则非叶子结点有n-1个,所以总共节点数目是2*n-1个。同时也要将剩下的n-1个双亲结点域初始化为0,这主要是为了查找权值最小的结点方便。

依次选择两个权值最小的结点,分别作为左子树结点和右子树结点,修改他们的双亲结点域,使它们指向同一个双亲节点,同时修改双亲结点的权值,使其等于两个左、右子树结点权值的和,并修改左、右孩子结点域,使其分别指向左、右孩子结点。重复执行这种操作n-1次,即求出n-1个非叶子结点的权值。这样就得到了一棵哈夫曼树。

通过求得的哈夫曼树,得到每一个叶子结点的哈夫曼编码。从叶子结点c开始,通过结点c的双亲结点域,找到结点的双亲,然后通过双亲结点左孩子域和右孩子域判断该结点c是其双亲结点的左孩子还是右孩子,如果是左孩子,则编码为‘0’,否则编码为‘1’。按照这种方法,直到找到根结点,即可以求出叶子结点的编码。

code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <iostream>
using namespace std;
#define infinity 10000
typedef struct
{
	unsigned int weight;
	unsigned int parent, lchild, rchild;
}HTNode, *HuffmanTree;
typedef char **HuffmanCode;
int Min(HuffmanTree t, int n);
void Select(HuffmanTree *t, int n, int *s1, int *s2);
void HuffmanCoding(HuffmanTree *HT, HuffmanCode *HC, int *w, int n);
void main()
{
	HuffmanTree HT;
	HuffmanCode HC;
	int *w, n, i;
	cout << "请输入叶子结点的个数:";
	cin >> n;
	w = (int *)malloc(n*sizeof(int));
	for (i = 0; i < n;i++)
	{
		cout << "请输入第" << i + 1 << "个结点的权值:";
		cin >> w[i];
	}
	HuffmanCoding(&HT, &HC, w, n);
	for (i = 1; i <= n;i++)
	{
		cout << "权值为" << w[i - 1] << "的哈夫曼编码:";
		cout << HC[i] << endl;
	}
	for (i = 1; i <= n;i++)
	{
		free(HC[i]);
	}
	free(HC);
	free(HT);

	system("pause");
}
void HuffmanCoding(HuffmanTree *HT, HuffmanCode *HC, int *w, int n)
{
	int m, i, s1, s2, start;
	unsigned int c, f;
	HuffmanTree p;
	char *cd;
	if (n<=1)
	{
		return;

	}
	m = 2 * n - 1;
	*HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));
	for (p = *HT + 1, i = 1; i <= n;i++,p++,w++)
	{
		(*p).weight = *w;
		(*p).parent = 0;
		(*p).rchild = 0;
		(*p).lchild = 0;
	}

	for (; i <= m;i++,p++)
	{
		(*p).parent = 0;
	}
	for (i = n + 1; i <= m;++i)
	{
		Select(HT, i - 1, &s1, &s2);
		(*HT)[s1].parent = (*HT)[s2].parent = i;
		(*HT)[i].lchild = s1;
		(*HT)[i].rchild = s2;
		(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;

	}
	*HC = (HuffmanCode)malloc((n + 1)*sizeof(char*));
	cd = (char*)malloc(n*sizeof(char));
	cd[n - 1] = '\0';
	for (i = 1; i <= n;i++)
	{
		start = n - 1;
		for (c = i, f = (*HT)[i].parent; f != 0;c=f,f=(*HT)[f].parent)
		{
			if ((*HT)[f].lchild==c)
			{
				cd[--start] = '0';
			} 
			else
			{
				cd[--start] = '1';
			}
			(*HC)[i] = (char*)malloc((n - start)*sizeof(char));
			strcpy((*HC)[i], &cd[start]);
		}

	}
	free(cd);
}


int Min(HuffmanTree t, int n)
{
	int i, flag;
	int f = infinity;
	for (i = 1; i <= n;i++)
	{
		if (t[i].weight<f&&t[i].parent==0)
		{
			f = t[i].weight, flag = i;

		}

	}
	t[flag].parent = 1;
	return flag;
}

void Select(HuffmanTree *t, int n, int *s1, int *s2)
{
	int x;
	*s1 = Min(*t, n);
	*s2 = Min(*t, n);
	if ((*t)[*s1].weight>(*t)[*s2].weight)
	{
		x = *s1;
		*s1 = *s2;
		*s2 = x;
	}
}

结果:

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值