关于哈夫曼树与编码,带权路径长度

哈夫曼树与哈夫曼编码的定义

typedef char** HuffmanCode;
typedef struct
{
	int weight;
	int parent;
	int LChild;
	int RChild;
}HTNode, HuffmanTree[10001];

建立哈夫曼树

建树前需要对树进行初始化,若有n个元素,则哈夫曼树一共会有2 * n - 1个元素,对1~n个位置置入对应的权值,剩下的全部置为0;同时还需要编写一个选择函数,选出当前情况下最小的两个数,总体思路是从下往上建树。

void select(HuffmanTree ht, int n, int &s1, int &s2)
{
	int min, _min;//min是最小值,_min是第二小的值,分别赋给左,右子树
	for (int i = 1; i <= n ;i++)
	{
		if (ht[i].parent == 0) {
			min = i; break;
		}
	}
	for (int i = min+1; i <= n; i++)
	{
		if (ht[i].weight < ht[min].weight && ht[i].parent == 0) {
			min = i;
		}
	}
	s1 = min;
	for (int i = 1; i <= n; i++)
	{
		if (ht[i].parent == 0 && i != s1) {
			_min = i;
			break;
		}
	}
	for (int i = _min + 1; i <= n; i++)
	{
		if (ht[i].weight < ht[_min].weight && i != s1 && ht[i].parent == 0) {
			_min = i;
		}
	}
	s2 = _min;
}
void CreHuffmanTree(HuffmanTree &ht, int w[], int n)
{
	for (int i = 1; i <= n; i++) ht[i] = { w[i],0,0,0 };
	int m = 2 * n - 1;
	for (int i = n + 1; i <= m; i++) ht[i] = { 0,0,0,0 };
	for (int i = n + 1; i <= m ; i++)
	{
		int s1, s2;
		select(ht, i - 1, s1, s2);
		ht[i].weight = ht[s1].weight + ht[s2].weight;
		ht[s1].parent = ht[s2].parent = i;
		ht[i].LChild = s1, ht[i].RChild = s2;
	}
}

哈夫曼树编码

由于编码是从上到下,但是遍历是从下到上,所以思路是先开一个指针做数组用,从后往前写,然后用strcpy函数复制到编码区里

void CreHuffmanCode(HuffmanTree &ht, HuffmanCode &hc, int n)
{
	hc = (char**)malloc(sizeof(char*) * n + 1);
	char* code = (char*)malloc(sizeof(char) * n);
	code[n - 1] = '\0';
	for (int i = 1; i <= n; i++)
	{
		int start = n - 1;
		int c = i;
		int p = ht[i].parent;
		while (p)
		{
			if (ht[p].LChild == c) code[--start] = '0';
			else code[--start] = '1';
			c = p;
			p = ht[c].parent;
		}
		hc[i] = (char*)malloc((n - start) * sizeof(char));
		strcpy(hc[i], &code[start]);
	}
	free(code);
}

记得free释放空间

通过译码求输入的序列

本来我是准备直接硬着匹配的,但是时间复杂度和空间复杂度都太高了,最后是用哈夫曼树的特性来求,从树顶开始向下走,如果是0就走左边,是1就走右边,走到叶子节点为结束:

void code(HuffmanTree ht, int n, string s)
{
	int i = 0;
	while (i<(signed)s.size())
	{
		int cur = 2 * n - 1;
		while (ht[cur].LChild != 0 && ht[cur].RChild != 0)
		{
			if (s[i] == '0') {
				cur = ht[cur].LChild;
			}
			else if (s[i] == '1') {
				cur = ht[cur].RChild;
			}
			i++;
		}
		char ch = 'a' + cur - 1;
		cout << ch;
	}
}

具体题目与完整代码:

建哈夫曼树,求哈夫曼编码,求解译码

问题描述】读入n个字符所对应的权值,自底向上构造一棵哈夫曼树,自顶向下生成每一个字符对应的哈夫曼编码,并依次输出。另,求解某字符串的哈夫曼编码,求解某01序列的译码。

【输入形式】输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。第二行中有n个用空格隔开的正整数,分别表示n个字符的权值,依次按照abcd…的默认顺序给出。然后是某字符串和某01序列。

【输出形式】前n行,每行一个字符串,表示对应字符的哈夫曼编码。然后是某字符串的哈夫曼编码,某01序列的译码。

【注意】保证每次左子树比右子树的权值小;如出现相同权值的,则先出现的在左子树,即下标小的在左子树。

【样例输入】

8
5 29 7 8 14 23 3 11

aabchg

00011110111111001

【样例输出】

0001
10
1110
1111
110
01
0000
001

000100011011100010000

acdef

#include <bits/stdc++.h>
using namespace std;
typedef char** HuffmanCode;
typedef struct
{
	int weight;
	int parent;
	int LChild;
	int RChild;
}HTNode, HuffmanTree[10001];
void select(HuffmanTree ht, int n, int &s1, int &s2)
{
	int min, _min;//min是最小值,_min是第二小的值,分别赋给左,右子树
	for (int i = 1; i <= n ;i++)
	{
		if (ht[i].parent == 0) {
			min = i; break;
		}
	}
	for (int i = min+1; i <= n; i++)
	{
		if (ht[i].weight < ht[min].weight && ht[i].parent == 0) {
			min = i;
		}
	}
	s1 = min;
	for (int i = 1; i <= n; i++)
	{
		if (ht[i].parent == 0 && i != s1) {
			_min = i;
			break;
		}
	}
	for (int i = _min + 1; i <= n; i++)
	{
		if (ht[i].weight < ht[_min].weight && i != s1 && ht[i].parent == 0) {
			_min = i;
		}
	}
	s2 = _min;
}
void CreHuffmanTree(HuffmanTree &ht, int w[], int n)
{
	for (int i = 1; i <= n; i++) ht[i] = { w[i],0,0,0 };
	int m = 2 * n - 1;
	for (int i = n + 1; i <= m; i++) ht[i] = { 0,0,0,0 };
	for (int i = n + 1; i <= m ; i++)
	{
		int s1, s2;
		select(ht, i - 1, s1, s2);
		ht[i].weight = ht[s1].weight + ht[s2].weight;
		ht[s1].parent = ht[s2].parent = i;
		ht[i].LChild = s1, ht[i].RChild = s2;
	}
}
void CreHuffmanCode(HuffmanTree &ht, HuffmanCode &hc, int n)
{
	hc = (char**)malloc(sizeof(char*) * n + 1);
	char* code = (char*)malloc(sizeof(char) * n);
	code[n - 1] = '\0';
	for (int i = 1; i <= n; i++)
	{
		int start = n - 1;
		int c = i;
		int p = ht[i].parent;
		while (p)
		{
			if (ht[p].LChild == c) code[--start] = '0';
			else code[--start] = '1';
			c = p;
			p = ht[c].parent;
		}
		hc[i] = (char*)malloc((n - start) * sizeof(char));
		strcpy(hc[i], &code[start]);
	}
	free(code);
}
void code(HuffmanTree ht, int n, string s)
{
	int i = 0;
	while (i<(signed)s.size())
	{
		int cur = 2 * n - 1;
		while (ht[cur].LChild != 0 && ht[cur].RChild != 0)
		{
			if (s[i] == '0') {
				cur = ht[cur].LChild;
			}
			else if (s[i] == '1') {
				cur = ht[cur].RChild;
			}
			i++;
		}
		char ch = 'a' + cur - 1;
		cout << ch;
	}
}

int main()
{
	int n, w[10001];
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> w[i];
	}
	HuffmanTree ht;
	CreHuffmanTree(ht, w, n);
	HuffmanCode hc;
	CreHuffmanCode(ht, hc, n);
	for (int i = 1; i <= n; i++)
	{
		cout << hc[i] << endl;
	}
	string sample;
	cin >> sample;
	for (int i = 0; i < sample.size(); i++)
	{
		int temp = sample[i] - 96;
		cout << hc[temp];
	}
	cout << endl;
	string s;
	cin >> s;
	code(ht, n, s);
}

带权路径长度

有两种方式求解,第一是求出每个叶子结点然后用权值乘以这个结点到树顶的长度,这种方法太麻烦。另一种方式是求所有非叶子结点权值的和,这里写第二种写法,这种办法甚至不需要建一颗哈夫曼树

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n, x;
	cin >> n;
	int temp1, temp2, sum = 0, y;
	priority_queue<int, vector<int>, greater<int> > q;
	while (!q.empty())
	{
		q.pop();
	}
	for (int i = 1; i <= n; i++)
	{
		cin >> x;
		q.push(x);
	}
	for (int i = 2; i <= n; i++)
	{
		temp1 = q.top();
		q.pop();
		temp2 = q.top();
		q.pop();
		y = temp1 + temp2;
		sum = sum + y;
		q.push(y);
	}
	cout << sum;
	return 0;
}

具体的数据结构采用优先队列(堆)每次出队都是最小的数据,满足哈夫曼树的定义.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值