哈夫曼树
Posted ticmis
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哈夫曼树相关的知识,希望对你有一定的参考价值。
数论 哈夫曼树
1.0 引子
A: 欸,你听说过“带权最优二叉树”嘛?就是“带权路径长度最短的二叉树“嘛?就是“路径上的分支个数称为路径长度”嘛?就是“叶子结点都有一定的权值”嘛?
B: 说人话
A: 《合并果子》..
1.1 二叉哈夫曼算法
也许算法并不叫这个名字,只是我觉得好记好懂就这么叫了..
二叉哈夫曼算法解决的问题与《合并果子》别无二致,但是为了严谨,还是重新叙述一遍二叉哈夫曼算法解决的问题:
有n个数;每一次合并操作代价为两个数字之和;每一次合并可以将两个数字合并为一个数字,其大小为原先两数字之和;试求最小合并代价
一个(O(nlogn))的优先队列解法很快浮现了出来。二叉哈夫曼算法复杂度与该解法同级,但没有利用到优先队列这个数据结构。
一、 开两个数组记为a[],和b[]。a[]数组初始化为升序的原数组,而b[]数组为空
二、 每一次尝试从a[]和b[]队首中找最小的两个数
三、 将其合并之后,放入b[]的队尾
1) a[]的单调性保证:初始化时排序,之后不再发生插入操作
2) b[]的单调性保证:每一次合并的结果将会越来越大,而越靠后的合并结果将会插在b[]越靠后的位置上,单调性得到保证
a[]和b[]都有单调性保证,只要从两个队列队首抽一个较小值,即可得到全局最小值
1.2 k叉哈夫曼算法
解决了二叉哈夫曼算法,不禁联想到将情况扩展到更高维的情况:假如同时允许合并k个数字呢?
事实上情况并没有变得太过复杂,优先队列解法可以很轻松的转移过来,同理“k叉哈夫曼算法”诞生了。与二叉哈夫曼算法几乎没有区别,只是每一次取前k小即可。
但是,每一次合并将会减少(k-1)个数字,假如最后一次合并没能凑齐k个数字呢?贪心地考虑一下,越靠后的合并操作,牵扯到的数字越多,因此合并操作越靠后,越应当凑齐正好k个数字,不能浪费机会。由于算法很明显是不能倒序进行的,因此我们只需要事先给数列添加一些人畜无害的"0"调整个数,便能保证最后一次合并可以凑齐k个数字整
虽然具体放"0"的个数很简单,做题的时候顺手推一下就能推出来,但是还是总结了以下公式:
一道裸的哈夫曼算法
2.1 哈夫曼树
哈夫曼树其实就是把在进行哈夫曼算法的同时,将k个受牵连的节点抽出来,新建一个点连向这k个节点。脑补一下,就能想象出这么做的后果是会形成一棵树吧..
e.p. 合并 {1,1,2,2} 的哈夫曼树
3.1 哈夫曼编码
哈夫曼编码的几条性质:
- 哈夫曼编码是一种可变字长编码,即每一个对应元素的编码长度不一定相同
- 哈夫曼编码是一种前缀编码,即一组编码中任一编码都不是其他任何一个编码的前缀
- 满足同等条件下,哈夫曼编码可以保证利用编码转译后,译文总长度最短
3.2 带权路径
先下一条定义:
带权路径:对于一条从根节点到叶节点的路径,其贡献等于叶节点的点权( imes)路径长度(所有边的长度为1)
一个常见的问题就是“带权路径和最小”。其实很好解决,由于在带权路径中叶节点的点权被扩大了路径长度次,那我们就将这些贡献原样的“分摊”给路径上的每一个节点。
容易发现,新的树其实就是哈夫曼树,“带权路径和最小”其实就可以利用哈夫曼算法得到解决
问题的解决
将问题整理一下:有n个k进制密钥,每一个密钥之间长度可以不等;第i个密钥出现了给定的a_i次;密钥之间彼此直接无歧义,即彼此间不是彼此的前缀;试问译文的最短长度
对于k进制密钥,就建一颗k叉树;将n个密钥出现次数作为a_i作为树的叶节点;除叶节点外的节点点权大小为该节点子节点的点权之和。
将每一个节点连向儿子的边顺序编号。由于是k叉树,因此标号大小严格不大于k,因此从根节点到叶节点的路径对应着一个长度为路径长度的k进制密钥,且密钥唯一,彼此不存在覆盖关系,也就不存在前缀关系。
将密钥问题转换为树上的路径问题后,原问题中“译文总长度最短”也就转换为了“带权路径”最短的问题。这便是哈夫曼树所解决的问题。
例题:[NOI2015]荷马史诗](https://www.luogu.org/problem/P2168)
子问题一就是哈夫曼编码的应用:对于子问题二,只需要略改哈夫曼算法:倘若两个队首数字相同,娶一个深度较小的即可
NOI水题一发AC祭 [ヾ(≧▽≦)o ](https://www.luogu.org/record/22383511)
以上是关于哈夫曼树的主要内容,如果未能解决你的问题,请参考以下文章