哈夫曼树和哈夫曼编码的实现

对于题目和规则不做赘述,进入正题:

要点:
1.逻辑上哈夫曼树是树结构,但程序的物理结构是数组。
2.树构建好后,规定一下左右是0还是1,然后遍历树的每条路径,即可得到每个数据的编码结果。

代码如下:

//哈夫曼编码
typedef struct mesage//在优先级队列中用到的结构体
{
	mesage(int index = 0, int weight = 0)
		:index(index), weight(weight)
	{}
	int index;//下标,用来标识是哪个叶节点
	int weight;//权重,根据这个调整堆
	operator int() const//相当于(int)mesage,用权重做比较
	{
		return weight;
	}
}Mesage;
typedef struct node//静态二叉树的节点信息
{
	node(int value = 0, int parent = 0, int left = 0, int right = 0)
		:value(value), parent(parent), left(left), right(right)
	{}
	Mesage msg;//下标和权重
	int value;//数值
	int parent;//双亲的下标
	int left;//左孩子的下标
	int right;//右孩子的下标
}Node;
typedef struct result//存储一个值的最终哈夫曼编码
{
	result(int value = 0)
		:value(value)
	{
		str = new char[20]();
	}
	~result(){delete[] str;}
	int value;//值
	char *str;//编码
}Result;
void displayTree(Node *table, int len)//显示静态哈夫曼树
{
	printf("index\tweight\tvalue\tparent\tleft\tright\n");
	for (int i = 0; i < len; ++i)
	{
		printf("%d\t%d\t%d\t%d\t%d\t%d\n",
			table[i].msg.index, table[i].msg.weight, table[i].value,
			table[i].parent, table[i].left, table[i].right);
	}
}
//给最终结果填写数据
void fillData(Node *table, int index_table, Result *result, int index_result,
			  char tempSpace[], int index_tempSpace)
{
	if (table[index_table].value != 0)//到达叶子节点,填写数据
	{
		for (int i = 0; i < index_tempSpace; ++i)
			result[index_result].str[i] = tempSpace[i];
		result[index_result].str[index_tempSpace] = '\0';
		result[index_result].value = table[index_table].value;
		return;
	}
	tempSpace[index_tempSpace] = '0';//向左走记为0
	fillData(table, table[index_table].left, result, table[index_table].left,
		tempSpace, index_tempSpace+1);
	tempSpace[index_tempSpace] = '1';//向右走记为1
	fillData(table, table[index_table].right, result, table[index_table].right,
		tempSpace, index_tempSpace+1);
}
void displayCode(Node *table, int len, int dataNumber)//根据静态哈夫曼树计算并显示结果
{
	Result *result = new Result[dataNumber+1];//最终结果数组
	char *tempSpace = new char[dataNumber+1];//辅助空间字符串,记录往左右走时的0和1
	fillData(table, len-1, result, 1, tempSpace, 0);

	for (int i = 1; i <= dataNumber; ++i)//打印结果
	{
		printf("value= %d, code= %s\n", result[i].value, result[i].str);
	}
	delete[] result;
	delete[] tempSpace;
}
//根据数据的权重和数值,构建静态哈夫曼树
void HuffmanTree(const int weight[], const int value[], int len)
{
	Node *table = new Node[2*len]();//用数组存储哈夫曼树结构
	for (int i = 1; i <= len; ++i)//填充叶子节点数据,从下标1开始
	{
		table[i].msg = Mesage(i, weight[i-1]);
		table[i].value = value[i-1];
	}

	//构建树过程:把叶子节点都先放到优先级队列中,设定堆为小顶堆
	priority_queue<Mesage, vector<Mesage>, greater<Mesage>> que;
	for (int i = 0; i < len; ++i)
		que.push(Mesage(i+1, weight[i]));
	//从集合中取两个权重最小的子树,给它们创建一个双亲节点,然后把新的子树放入集合中
	//循环这一过程,直到把所有叶子节点全部合并到树上,树构建完成
	int offest = len+1;
	for (int i = 0; i < len-1; ++i)
	{
		int index = offest + i;
		Mesage left = que.top();
		que.pop();
		Mesage right = que.top();
		que.pop();

		table[index].msg.index = index;
		table[index].msg.weight = left.weight + right.weight;
		table[index].left = left.index;
		table[index].right = right.index;
		table[left.index].parent = index;
		table[right.index].parent = index;
		que.push(Mesage(index, table[index].msg.weight));
	}

	displayTree(table, 2*len);
	cout << endl;
	displayCode(table, 2*len, len);
	delete[] table;
}
int main()
{
	int weight[] = {8,3,4,1,5,9,6,7,2};//数据权重
	int len = sizeof(weight) / sizeof(weight[0]);
	int *value = new int[len]();//数据值
	for (int i = 0; i < len; ++i)
		value[i] = weight[i] * 10;

	HuffmanTree(weight, value, len);
	delete[] value;
	return 0;
}

结果如下:(简单起见,数据的值和权重一致,权重=值/10)(可见树的辅助节点=叶子节点-1,最终树节点个数=数据个数*2-1。因此创建数组时大小确定,为数据的2倍。先把9个数据放在前面,然后构建树时辅助节点依次放在后面。)(数据节点的左孩子和右孩子为空,辅助节点的值为空。)
运行结果
下面以一个小规模情况说明逻辑过程:
假设
weight={2,1,3}
value={20,10,30}
1.先把全部数据都放到集合中。(圆圈内部是数据,下方是权重)
在这里插入图片描述

2.拿出权重最小的两个子树:1和2,组成树
在这里插入图片描述

3.把树放入集合中
在这里插入图片描述

4.拿出两个权重最小的两个子树:3和3,组成树
在这里插入图片描述

5.集合为空,树构建完成。
6.遍历树(左0右1)
在这里插入图片描述
解题完成,
10的哈夫曼编码是:00
20的哈夫曼编码是:01
30的哈夫曼编码是:1

可以看到,数据权重越大,越靠近跟节点,哈夫曼编码的长度越小,越节省空间,所以哈夫曼编码在压缩算法中广泛应用,压缩效果良好。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值