哈夫曼树与哈夫曼编码

哈夫曼树

哈夫曼树是一种二叉树,其带权路径(叶子结点的权重与其到根节点的路径长度之积)和最小。

通俗一点说,哈夫曼树的每个叶子结点都有一个权重,而每个叶子结点的带权路径,就是其权重与到根节点的距离的乘积。哈夫曼树要求所有叶子结点的带权路径和最小。

怎么构造?虽说哈夫曼树是一个二叉树,但是可以根据二叉树的原理扩展为k叉树,下面先介绍二叉树的构造方法,k叉树就显而易见了。

运用贪心的思想,肯定是权重越大的节点越靠近根节点是最优的。我们可以先将权重排序,由小到大、由下及上构造。每次选取权重最小的两个节点,将其连接到一个父节点上,父节点的权重为子节点权重之和,然后将父节点放回到数组中,重复这个过程,直到构造出树。

考虑k叉树,显然每次选取k个最小的连接到一个父节点上。但是k叉会有一个问题:每一个节点的子节点并不是满k叉的(读者可自证为什么二叉树不会有这个问题),但是反观我们的构造方法,是优先构造底层,而显然越靠近根节点的层越满越优,所以之前的构造方法就会有问题。这里就需要将不够的地方补齐,手动添加一下叶子结点,至于其权重,弄成0就好啦。

哈夫曼编码

考虑一个k叉树,根据每一层选择的节点不一样,可以表示出k进制数,哈夫曼编码便是运用这个道理。将每一个需要编码的元素以其出现次数为权重,构建哈夫曼树,自然编码也就出来了。

代码网上比较多,这里就不放了,放一道哈夫曼意识流的题qwq

[[NOI2015] 荷马史诗]([P2168 NOI2015] 荷马史诗 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
long long read() {
	long long x = 0; int f = 0; char c = getchar();
	while (c < '0' || c > '9') f |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c - '0'), c = getchar();
	return f ? -x : x;
}
int n, k;
struct szh {
	int h;
	long long w;
	szh(long long x, int y){
		w = x; h = y;
	}
	bool operator < (const szh b) const {
		if (w != b.w) return w > b.w;
		return h > b.h;
	}
};
long long ans;
priority_queue<szh> q;
int main() {
	n = read(); k = read();
	for (int i = 1; i <= n; ++i) {
		long long x = read();
		q.push(szh(x, 1));
	}
	int cnt = 0;
	if ((n - 1) % (k - 1)) cnt = k - 1 - (n - 1) % (k - 1);
	for (int i = 1; i <= cnt; ++i) q.push(szh(0, 1));
	cnt += n;
	while (cnt > 1) {
		long long sum = 0;int height = 0;
		for (int i = 1; i <= k; ++i) {
			sum += q.top().w;
			height = max(height, q.top().h);
			q.pop();
		}
		q.push(szh(sum, height + 1));
		ans += sum;
		cnt -= k - 1;
	}
	cout << ans << endl << q.top().h - 1;
}
posted @ 2021-07-25 21:27  Kylin_Seven  阅读(223)  评论(0编辑  收藏  举报