哈夫曼树学习笔记

定义

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
——百度百科

应用

信息传递中的编码问题:使编码长度尽可能短又不产生歧义。

歧义:两个编码有公共前缀部分,比如: \(001\) , \(0010\)

构造方法

  1. 设有 n 个树,初始时每颗树只有一个叶子节点(有权值),代表一种编码情况,权值代表出现次数。

  2. 每次选取权值和最小的两颗树,将它们合并成一棵树,新树权值和为原来两颗树权值之和。

  3. 重复 2. 直至只有一颗树。即为哈夫曼树。

实现方法

我们可以用小根堆堆来维护权值最小的树,利用贪心求解。

例题

题目

洛谷 P2168 [NOI2015]荷马史诗

思路

这道题要求构造 \(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('\n');
	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;
}
posted @ 2020-07-14 21:57  Werner_Yin  阅读(143)  评论(0编辑  收藏  举报