定义
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
——百度百科
应用
信息传递中的编码问题:使编码长度尽可能短又不产生歧义。
歧义:两个编码有公共前缀部分,比如: (001) , (0010)。
构造方法
-
设有 n 个树,初始时每颗树只有一个叶子节点(有权值),代表一种编码情况,权值代表出现次数。
-
每次选取权值和最小的两颗树,将它们合并成一棵树,新树权值和为原来两颗树权值之和。
-
重复 2. 直至只有一颗树。即为哈夫曼树。
实现方法
我们可以用小根堆堆来维护权值最小的树,利用贪心求解。
例题
题目
思路
这道题要求构造 (k) 叉哈夫曼树,并使树的高度最小,并输出其高度
因为每次减少 (k-1) 个树,最后有些节点可能为空,应该补上一些空节点。
同时排序时要注意当权值相同时,让高度小的树排在后面。
代码
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
#define re(x) read(x)
#define ll long long
#define mes(x) memset(x,0,sizeof(x))
#define il inline
#define _for(i,a,b) for(int i = (a);i < (b);i++)
#define _rep(i,a,b) for(int i = (a);i <= (b);i++)
using namespace std;
template <typename T>
il void read(T &x){
x = 0;
T sgn = 1;
char ch = getchar();
for(;!isdigit(ch);ch = getchar()) if(ch == '-') sgn = -1;
for(;isdigit(ch);ch = getchar()) x = (x<<1) + (x<<3) +(ch^48);
x *= sgn;
}
template <typename T>
il void write(T x){
if(x<0) x = -x , putchar('-');
if(x == 0) putchar(48);
int cccnt = 0 , a[70];
while(x>0) {
a[++cccnt] = x%10;
x /= 10;
}
for(int i = cccnt;i > 0;i--) putchar(a[i]+48);
putchar('
');
return;
}
int n,k,cnt;
ll ans;
struct root{
ll tot,h;
bool operator < (const root &b) const {
if(tot == b.tot) return h > b.h;
return tot > b.tot;
}
};
priority_queue <root> q;
int main (){
re(n); re(k);
_for(i,0,n){
ll w;
re(w);
q.push((root){w,0});
}
if((n-1)%(k-1) != 0){
cnt += k-1 - (n-1)%(k-1);
_for(i,0,cnt)
q.push((root){0,0});
}
cnt = n;
while(cnt > 1){
ll maxn = 0 ,tot = 0;
_for(i,0,k){
tot += q.top().tot;
maxn = max(maxn,q.top().h);
q.pop();
}
ans += tot;
q.push((root){tot,maxn+1});
cnt -= k-1;
}
write(ans);
write(q.top().h);
return 0;
}