首页 > 其他分享 >基本技巧——哈夫曼树 学习笔记

基本技巧——哈夫曼树 学习笔记

时间:2024-06-17 10:35:36浏览次数:11  
标签:技巧 哈夫曼 int ll 笔记 叶子 权值 节点

基本技巧——哈夫曼树 学习笔记

概念

一棵包含有 \(n\) 个叶子节点的 \(k\) 叉树,其中第 \(i\) 个叶子节点带有权值 \(W_i\)。

树的带权路径长度,定义为从根结点到各叶结点的路径长度与相应叶节点权值的乘积之和。

树的带权路径长度,记为 WPL(Weighted Path Length of Tree),公式表示:

\[\text{WPL}=\sum_{i=1}^nW_iL_i \]

在一组有确定权值的叶子节点中,可以构造出不同的 \(k\) 叉树。

其中,WPL 最小的 \(k\) 叉树,称为 \(k\) 叉哈夫曼树(Huffman 树)。

哈夫曼算法

容易发现,对于哈夫曼树,权值越小离根越远,反之,权值越大深度越小。

因此,容易得出,仅有叶子节点的度为 \(0\),其他节点的度均为 \(k\)。

证明:如果其他节点的度小于 \(k\),那么意味着下面的节点放到这个空位,结果更优。

因此,我们需要额外添加一些权值为 \(0\) 的叶子节点,使得叶子节点个数 \(n\) 满足,

\[n-1\equiv0\pmod{k-1} \]

这样,我们就可以保证空的位置,会放在叶子上而不是非叶子节点,使得贪心正确。

算法流程

  • 初始化:将给定的 \(n\) 个叶子节点,直接连到根上,共 \(n+1\) 个节点。

  • 合并:从二叉树中选取两个权值和最小的节点,合并为一个新的节点。

  • 重复合并操作,直至只剩 \(k-1\) 个节点,所得二叉树即为哈夫曼树。

如果需要所得的最长编码最短,则还需要优先合并当前层数少的节点。

模板题

链接:P2168 [NOI2015] 荷马史诗

模板题,模拟即可,

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e5 + 10;

using ll = long long;

struct emm {
	ll w; int h;
	emm() = default;
	emm(ll w, int h): w(w), h(h) {}
	friend bool operator <(const emm &a, const emm &b) {
		return a.w == b.w ? a.h > b.h : a.w > b.w;
	}
};

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	int n, k; cin >> n >> k; priority_queue<emm> heap;
	ll w; for (int i = 1; i <= n; ++i) cin >> w, heap.emplace(w, 1);
	while ((heap.size() - 1) % (k - 1) != 0) heap.emplace(0, 1);
	ll ans = 0; auto merge = [&] {
		int h = 0; ll w = 0;
		for (int i = 1; i <= k; ++i) h = max(h, heap.top().h), w += heap.top().w, heap.pop();
		ans += w, heap.emplace(w, h + 1);
	};
	while (heap.size() >= k) merge();
	cout << ans << endl << heap.top().h - 1 << endl;
	return 0;
}

简单题

链接:P1090 [NOIP2004 提高组] 合并果子

容易发现,合并顺序即叶子节点的深度,于是哈夫曼编码。

权值小的节点优先合并即可,代码:

#include <bits/stdc++.h>

using namespace std;

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n; cin >> n;
    priority_queue<int, vector<int>, greater<int>> d;
    for (int i = 0, x; i < n; ++i) cin >> x, d.push(x);
    int ans = 0;
    while (d.size() > 1) {
    	int a = d.top(); d.pop();
    	int b = d.top(); d.pop();
    	ans += a + b, d.push(a + b);
    }
    cout << ans << endl;
	return 0;
}

标签:技巧,哈夫曼,int,ll,笔记,叶子,权值,节点
From: https://www.cnblogs.com/RainPPR/p/18251870

相关文章

  • 开源复刻apple 数学笔记;纯C++实现了ChatGLM系列模型;腾讯混元文生图模型发布新版本并开
    ✨1:AIMathNotesAIMathNotes是一个交互式绘图应用,可绘制并计算数学方程。AIMathNotes受到Apple在WWDC2024上的“MathNotes”演启发,开发的一个互动式绘图应用程序,用户可以在画布上绘制数学方程。一旦方程被绘制完成,应用程序将使用多模态LLM(LargeLanguageM......
  • 快慢指针技巧
    快慢指针技巧在说快慢指针之前,我们先说一下双指针。双指针双指针:使用两个指针来解决问题。所谓的指针其实就是指数组的下标,或者链表的节点的地址。我们以数组为例介绍一下。有两个指针分别存储着数组的两个下标,这就是双指针。那快慢指针是什么呢?快慢指针快慢指针,就是一......
  • spring boot(学习笔记第八课)
    springboot(学习笔记第八课)数据库操作-MyBatis,SpringDataJPA,多数据源学习内容:数据库操作-MyBatis数据库操作-SpringDataJPA多数据源(JdbcTemplate)1.数据库操作-MyBatisspringboot的操作有JdbcTemplate,MyBatis,SpringDataJPA主要这三个包。其中,JdbcTempla......
  • 《构建之法》阅读笔记3
    在探讨软件工程的实践方法后,《构建之法》一书还着重探讨了软件架构的设计与演化。作者认为,良好的软件架构是确保软件质量和可持续发展的关键所在。首先,作者阐述了软件架构设计的重要性。软件架构决定了软件系统的结构和特性,是软件开发的基础。良好的架构设计应该遵循关注点分......
  • 《构建之法》阅读笔记2
    除了软件工程的核心要素,《构建之法》一书还深入探讨了敏捷开发、持续集成等软件开发实践方法。这些实践方法有助于提高软件开发的效率和响应速度。首先,作者阐述了敏捷开发方法的核心价值观和原则。敏捷开发强调以客户需求为中心,通过迭代和反馈的方式快速交付价值。相比传统的......
  • 《梦断代码》阅读笔记2
    在《梦断代码》这本书中,作者通过讲述程序员小明的故事,反映了当代社会中技术与人性的矛盾。小明是一个技术精湛的程序员,但在工作中却常常感到孤独和焦虑。他不断追求完美的代码,却陷入了技术和人际问题的泥潭中。小明的故事让我深刻地思考了技术与人性的关系。技术的发展带来了巨大......
  • 《梦断代码》阅读笔记1
    在《梦断代码》这本书中,作者通过讲述程序员小明的故事,揭示了现代社会中人与技术、人与人之间的关系。小明是一个在IT行业工作多年的程序员,他对技术有着极高的热情和执着,但在工作中却常常感到孤独和压力。他的梦想是创造一个完美的代码,但现实却让他不断遭遇挫折和困难。在小明的故......
  • 《梦断代码》阅读笔记3
    《梦断代码》这本书讲述了程序员小明在追求完美代码的过程中所遇到的技术和人际问题。小明是一个技术精湛的程序员,但在工作中却常常感到孤独和困惑。他不断追求完美的代码,却陷入了技术和人际问题的泥潭中。小明的故事让我深刻地认识到了技术与人性之间的矛盾和冲突。技术的发展带......
  • Unity学习笔记----摄像机组件信息相关知识点总结
    一.ClearFlags1.skybox天空盒一般用于3d游戏。2.SolidColor颜色填充一般用于2d游戏。3.Depthonly只画该层,背景透明与Depth配合使用,等会再写。4.Don'tClear不移除,渲染覆盖不会擦除上一帧的画面,一般不使用。默认二.CullingMask选择性渲染部分层级,可以指定渲染对......
  • C语言笔记第15篇:文件操作
    1、为什么使用文件?如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。2、什么是文件?磁盘(硬盘)上的文件就是文件。但是程序设计中,我们一般谈两个文......