哈夫曼樹學習筆記
阿新 • • 發佈:2020-07-14
定義
給定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('\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; }