哈夫曼树

定义

带权路径长度

节点权值乘点到根节点的距离。

哈夫曼树

树上所有节点带权路径长度之和最小的二叉树。

性质

  • 满二叉树
  • 原序列构成哈夫曼树的所有叶子节点
  • 离根节点越近,点权越大
  • 非叶子节点的点权之和就是所有叶子节点的带权路径之和
  • 哈夫曼树总节点数为 \(2n - 1\)\(n\) 为叶子节点数量

哈夫曼编码

统计每个字母出现次数,以出现次数为权值构建哈夫曼树。

例题

P1090 [NOIP 2004 提高组] 合并果子

实质上是哈夫曼树的构建。

P2168 [NOI2015] 荷马史诗

题意:给定字符串和进制,求:

  • \(k\) 叉哈夫曼树带权路径和
  • 在合法的哈夫曼树中,最小的深度是多少

分析:

  1. \(k\) 叉哈夫曼树每 \(k\) 个节点合并成一个节点,若最后一次合并不足 \(k\) 个,说明有节点可以深度更小,带权路径长度可以更小
  2. 转化成哈夫曼树的根一定要连 \(k\) 条边才最优
  3. 考虑初始给哈夫曼树补叶子节点,且权值为 \(0\)
  4. \(k\) 个节点合并成一个节点,相当于每次减少 \(k - 1\) 个节点,因而总节点数 \(n\) 要满足 \(n \equiv 1 (\mod (k - 1))\)
  5. 当最小权值节点不止 \(2\) 个时,哈夫曼树形态不唯一,此时应该用高度作为排序第二关键字处理合并,最终的高度即可保证最小

建树代码:

cin >> n >> k;
for(int i = 1 ; i <= n ; ++ i)
	cin >> x, q.push(Node(x, 1));

while(k != 2 && q.size() % (k - 1) != 1) q.push(Node(0, 1));

while(q.size() >= k) {
	int res = 0, tmp = 0;

	for(int i = 1 ; i <= k ; ++ i)
		res += q.top().x, tmp = max(tmp, q.top().y + 1), q.pop();

	ans += res;

	q.push(Node(res, tmp));
}

int ansdep = q.top().y - 1;

cout << ans << '\n' << ansdep;

重载代码:

struct Node {
	int x, y;
	Node(int xx = 0, int yy = 0) {x = xx, y = yy;}
	inline bool operator < (const Node &A) const{
		if(x == A.x) return y > A.y;
		return x > A.x;
	}
};
priority_queue<Node> q;
posted @ 2025-03-29 13:53  endswitch  阅读(63)  评论(0)    收藏  举报