哈夫曼树(Huffman树) 学习日记 + 例题(ch1701 bzoj4198)
哈夫曼树:
一棵包含n个叶子节点的k叉树,其中第i个叶子节点带有权值w[i], l[i]为该点到根节点的距离,求所有叶子节点的w[i]*l[i]之和的最小值。
二叉哈夫曼树的求解:
对于二叉哈夫曼树的求解,我们贪心的将最小的两个节点放到最远,然后合并那两个节点,新的点的权值为那两个节点的和,放入原序列中,重复上述步骤,直到原序列只剩下一个节点,这棵树就构建好了,下面有个例子
例如: 3 4 5 8 , 设最后答案为ans
首先我们选3 4, 合并节点,新点权值为7,并加入原序列,ans+= (3+4)
然后新序列中合并5和7,新点权值为12 ans+=(5+7)
最后合并12 和 8 新节点为20,跳出循环 ans+=(12+8)
最后的哈夫曼树就是右边黑色的那棵树,答案就是ans
对于某一个节点,因为其被合并之后的值给了新的节点,而新的节点合并的时候又会加上这个值,实际上是不断为答案作贡献的,做贡献次数就等于深度(也就是路径长度)
k叉哈夫曼树的求解:
对于k(k>2)叉哈夫曼树,其求解思路和2叉类似。
我们合并2叉哈夫曼树是从子节点一路合并到根节点的,结束合并操作的标志就是序列中只剩下一个数。但是对于k叉哈夫曼树这会出现问题。
因为最大的节点应该优先连在根节点上,而我们求解时又是从叶子节点开始,这会造成最后根节点的子树少于k个,这是不对的。
比如: 3叉哈夫曼树,序列:3 4 5 8
第一次合并,合并3 4 5.
第二次合并,合并剩下的,最后这棵树就是:
这显然不是最优解。
因此对于k叉哈夫曼树,为了保证其根节点可以选到k个子树,假设节点个数为n,需要满足(n-1)mod(k-1)==0 的条件,假如不满足,我们为原序列补0
对于3 4 5 8 , 它就变成 3 4 5 8 0
第一步合并3 4 0 ,新节点为7,新序列: 7 5 8
然后合并7 5 8
最后生成的树:
例题1: ch1701
题目链接:https://www.acwing.com/problem/content/150/
这就是一棵裸的二叉哈夫曼树,每合并一次相当于最后答案计算的时候多加一份,就相当于在边长为1的哈夫曼树中更深一层
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 7;
int heap[maxn];
int cnt;
void del() {swap(heap[1], heap[cnt--]);int now = 1, to = 2;while (to <= cnt) {if (to<cnt&&heap[to]>heap[to + 1]) to++;if (heap[to] >= heap[now]) break;swap(heap[to], heap[now]);now = to;to = now << 1;}
}
void insert(int x) {heap[++cnt] = x;int now = cnt;while (now > 1) {if (heap[now >> 1] <= heap[now]) break;swap(heap[now], heap[now >> 1]);now >>= 1;}
}
int main() {int n; cin >> n;for (int i = 1; i <= n; i++) {int inp;scanf("%d", &inp);insert(inp); //我用二叉堆维护每次选最小值}int ans = 0;int min1, min2;while (cnt > 1) {min1 = heap[1];del();min2 = heap[1];del();ans += min1 + min2;insert(min1+min2);}cout << ans << endl;return 0;
}
例题2:bzoj4198
https://www.lydsy.com/JudgeOnline/problem.php?id=4198
每种编码方式前缀不同可以联想到字典树,此时每一个叶子节点就代表一个编号。
可以将哈夫曼树看成这样的字典树,而对于每个单词最后的贡献为: 单词哈希后长度*出现次数 ,对应到哈夫曼树里面就是 子节点权值=出现次数 , 深度=单词哈希后长度。
最后还要让最长的哈希最短,也就是要这颗哈夫曼树最深的子节点最浅,因此我们在合并的时候,假如遇到权值一样的,优先和深度小的合并(不要让深度大的继续合并下去了)
具体操作的话我们可以使用优先队列(当然堆都一样),然后用一个pair存 ,pair.first= 节点的权值 pair.second=节点的深度,优先队列按照first排,first一样再按second排
这就是一棵k叉哈夫曼树
#include<bits/stdc++.h>
#define ll long long
using namespace std;
typedef pair<ll, int> P;
const int INF = 0x3f3f3f3f;
struct cmp {bool operator()(const P p1, const P p2) {if (p1.first != p2.first) return p1.first > p2.first;else return p1.second > p2.second;}
};
priority_queue<P, vector<P>, cmp> Q;
int main() {ll ans = 0;int n, k;cin >> n >> k;for (int i = 1; i <= n; i++) {ll inp;scanf("%lld", &inp);Q.push(P(inp,0));}while ((n - 1) % (k - 1) != 0) { //k叉哈夫曼树补0Q.push(P(0, 0));n++;}while (Q.size() > 1) {P p = Q.top();Q.pop();for (int i = 2; i <= k; i++) { //每次取k,然后合并P p1 = Q.top();Q.pop();p.first += p1.first;p.second = max(p.second, p1.second); }p.second++;//深度为子节点里最深的+1ans += p.first;Q.push(p);}P p = Q.top();cout << ans << endl << p.second << endl;
}
哈夫曼树(Huffman树) 学习日记 + 例题(ch1701 bzoj4198)相关推荐
- 数据结构C#版笔记--啥夫曼树(Huffman Tree)与啥夫曼编码(Huffman Encoding)
哈夫曼树Huffman tree 又称最优完全二叉树,切入正题之前,先看几个定义 1.路径 Path 简单点讲,路径就是从一个指定节点走到另一个指定节点所经过的分支,比如下图中的红色分支(A-> ...
- 贪心算法【区间调度】【背包问题】【集合覆盖】【旅行商问题】【哈夫曼构造价值树】
贪心算法 在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解. 贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择, ...
- 数据压缩之经典——哈夫曼编码(Huffman)
(笔记图片截图自课程Image and video processing: From Mars to Hollywood with a stop at the hospital的教学视频,使用时请注意 ...
- 哈夫曼算法(huffman algorithm C)
哈夫曼算法:树的权值*路径相加最小. #include<stdio.h> #define size 10 typedef struct huffman {int weight;int lc ...
- 哈夫曼(Huffman)编码在word2vec中的应用
哈夫曼(Huffman)编码 假设给定 n 个权值{w1,w2,...,wn}作为二叉树的 n 个叶子结点,若二叉树的带权路径长度达到最小, 则称这样的二叉树为最优二叉树,也称为 Huffinan 树 ...
- 霍夫曼树:霍夫曼编码(Huffman Tree:Huffman Coding)
一.简介 霍夫曼树常处理符号编写工作.根据整组数据中符号出现的频率高低,决定如何给符号编码.如果符号出现的频率越高,则给符号的码越短,相反符号的号码越长. 相关术语 路径:从书中一个节点到另一个节点之 ...
- 【数据结构】哈夫曼编码和树
新知识: 转载博客:http://blog.163.com/sdnu_et/blog/static/13184636920100574953335/ 哈夫曼树:建一棵树,使每个叶子节点的点权与深度的乘 ...
- 哈夫曼编码(Huffman)Java实现代码简化版
这个网上发现的Huffuman编码Java实现在组织上相对简化,便于理解文件压缩过程:提取文件统计字符频度-根据字符频度创建huffman树-根据huffman树生成huffman可变字长无前缀编码- ...
- python哈夫曼编码注意_Python 算法(2) 哈夫曼编码 Huffman Encoding
这个问题原始是用来实现一个可变长度的编码问题,但可以总结成这样一个问题,假设我们有很多的叶子节点,每个节点都有一个权值w(可以是任何有意义的数值,比如它出现的概率),我们要用这些叶子节点构造一棵树,那 ...
最新文章
- 中小型局域网规划实战案例
- 并发编程——线程——Thread对象的属性和方法
- .NET Core 部署IIS无法启动Hangfire方案
- [Leetcode][第111题][JAVA][BFS][二叉树的最小深度][BFS][递归]
- 淘宝网架构分享总结[转]
- python3.8入门教程完整版_Python 3.8从入门到精通(视频教学版)
- SQL入门基础视频教程-Visual Foxpro视频教程
- 测试方法——正交表法
- 数据库导出Excel乱码 解决
- 海思Hi3796MV200最新官方SDK
- [TF进阶] 循环神经网络
- 显卡mx150和230哪个好_MX130与MX150差距对比分析
- 基于BS架构的微博系统
- 大数据下的密码学技术挑战
- Sentence Centrality Revisited for Unsupervised Summarization
- 四十二、Fluent欧拉模型流化床模拟
- 日志(Logger)
- ffmpeg缩放视频尺寸
- 计算机视觉之图像分割——水平集方法_ACWE2001
- 年产2万吨山楂酒工厂的设计-装瓶工段及车间的设计(lunwen+任务书+开题+选题表+cad图纸)