一:背景在**哈夫曼前,先來看乙個生活例項:將學生的百分制成績轉為等級制,即
[90 , 100]為a;
[80 , 90)為b;
[70 , 80)為c;
[60 , 70)為d;
[0 , 60)為e。
**實現為:
if (a < 60)
else if (a < 70)
else if (a < 80)
else if (a < 90)
else
我們把**表現的更形象點,繪出它對應的判別樹:
因為在實際生活中,學生的成績在這5哥等級上的分布是不均勻的,假設其分布規律如下:
分數0-59
60-69
70-79
80-89
90-100
比例0.05
0.15
0.40
0.30
0.1
如果學生的總成績資料有10000條,則5%的資料需 1 次比較,15%的資料需 2 次比較,40%的資料需 3 次比較,40%的資料需 4 次比較,因此 10000 個資料比較的
次數為: 10000 (5%+2×15%+3×40%+4×40%)=31500次
但是如果我們把判別樹稍微修改下,也就是對等級的判斷順序改變下,又如何呢?
此種形狀的二叉樹,需要的比較次數是:10000 (3×20%+2×80%)=22000次,顯然:兩種判別樹的效率是不一樣的。
問題是能不能找到一種效率最高的判別樹呢?
那就是接下來要講的哈夫曼樹。
二:哈夫曼樹哈夫曼樹(huffman),又叫最優樹,是一類帶權路徑長度最短的樹。
它們的帶權路徑長度分別為:
圖a: wpl=5*2+7*2+2*2+13*2=54
圖b: wpl=5*3+2*3+7*2+13*1=48
可見,圖b的帶權路徑長度較小,我們可以證明圖b就是哈夫曼樹(也稱為最優二叉樹)。
那麼我們如何構造哈夫曼樹?
一般可以按下面步驟構建:
1:將所有左,右子樹都為空的作為根節點。
2:在森林中選出兩棵根節點的權值最小的樹作為一棵新樹的左,右子樹,且置新樹的附加根節點的權值為其左,右子樹上根節點的權值之和。注意,左子樹的權值應小於右子 樹的權值。
3:從森林中刪除這兩棵樹,同時把新樹加入到森林中。
4:重複2,3步驟,直到森林中只有一棵樹為止,此樹便是哈夫曼樹。
下面是構建哈夫曼樹的**過程:
三:哈夫曼編碼目前。進行快速遠距離通訊的主要手段是電報,即將需傳送的文字轉換長由二進位制的字元組成的字串。例如,假設需傳送的電文為"abaccda",它只有4種字元,只需兩個字元的串便可分辨。假設a,b,c,d的編碼分別是00,01,10和11,則上述7個字元的電文便是「00010010101100」,總長14位,對方接收時,可按兩位一分進行解碼。
當然,在傳送電文時希望電文長度盡可能的短。如果對每個字元設計長度不等的編碼,且讓電文中出現次數較多的字元採用盡可能短的編碼,則傳送的電文總長度便可減少。
如果設計a,b,c,d的編碼是0,00,1,01,則上述7個字元可轉化為長度為9的字串「000011010」。但是,這樣的電文無法翻譯,例如傳送過去的字串中前4個字元的字串「0000」就有多種譯法,或是「aaaa」,或是「aba」,或是「bb」等等。因此,若要設計長短不等的編碼,則必須滿足:任意乙個字元編碼不是另乙個字元編碼的字首,這種編碼稱字首編碼。
可以用二叉樹來設計二進位制的字首編碼,如此得到必是二進位制字首編碼,讀者可以自己證明;同時,我們可以利用設計一棵哈夫曼樹的思想來使電文長度最短。
樹中從根到每個葉子節點都有一條路徑,對路徑上的各分支約定指向左子樹的分支表示」0」碼,指向右子樹的分支表示「1」碼,取每條路徑上的「0」或「1」的序列作為各個葉子節點對應的字元編碼。
就拿上圖例子來說:
a,b,c,d對應的哈夫曼編碼分別為:111,10,110,0
用圖說明如下:
四:哈夫曼編碼實現由於哈夫曼樹中沒有度為1的結點,則一棵有n個葉子的哈夫曼樹共有2n-1個結點,可以用乙個大小為2n-1 的一維陣列存放哈夫曼樹的各個結點。 由於每個結點同時還包含其雙親資訊和孩子結點的資訊,所以構成乙個靜態三叉鍊錶。
#define _crt_secure_no_deprecate
#define _crt_secure_cpp_overload_standard_names 1
#include#include//use string
#include//use strcpy
using namespace std;
typedef struct
htnode, *huffmantree;//哈夫曼樹
typedef char** huffmancode;//哈夫曼編碼表
void select(huffmantree & ht, int end, int & s1, int & s2);
void huffmancoding(huffmantree & ht, huffmancode & hc, int * w, char *ch, int n);
void huffmandecoding(huffmantree &ht, string text, int n);
int main()
/* 在ht[0...end]選擇parent為0且weight最小的兩個節點,其序號分別為是s1,s2(s1是最小weight節點的序號)
說白了,就是求出乙個陣列的最小的兩個值的下標
*/void select(huffmantree & ht, int end, int & s1, int & s2)
} }for (int i = 0; i <= end; i++)
}} cout << s1 << " " << s2 << endl;
}/* w存放n個字元的權值(均大於0),構造哈夫曼樹ht,並求出n個字元的哈夫曼編碼hc,ch陣列存放n個字元 */
void huffmancoding(huffmantree & ht, huffmancode & hc, int * w, char * ch, int n)
; for (; i < m; i++, p++)
*p = ;
int s1, s2;
for (i = n; i < m; i++)
//從葉子到根逆向求每個字元的哈夫曼編碼
int start;
hc = new char*[n];
char * cd = new char[n];
cd[n - 1] = '\0';
for (int i = 0; i < n; i++)
hc[i] = new char[n - start];
strcpy(hc[i], &cd[start]);
} deletecd;
}/* 根據編譯出的哈夫曼編碼,譯成字元明文 */
void huffmandecoding(huffmantree &ht, string text, int n)
}}
執行截圖:
資源,參考來自:
返回目錄---->資料結構與演算法目錄
資料結構 哈夫曼樹與哈夫曼編碼
1 路徑 由乙個結點到另乙個結點之間的所有分支共同構成。2 路徑長度 結點之間的分支數目。3 樹的路徑長度 從樹的根結點到其他所有結點的路徑長度之和。4 權 賦予某一實體的值。在資料結構中,實體包括結點和邊,所以對應有結點權和邊權。5 結點的帶權路徑長度 結點與樹的根結點之間的路徑長度與結點權的乘積...
資料結構 哈夫曼樹與哈夫曼編碼
include pch.h include 哈夫曼樹類huffmantree的定義 huffman樹結點類treenode宣告 template class t class huffmannode 建構函式 huffmannode getleft void const void setleft hu...
資料結構 哈夫曼樹 哈夫曼編碼
哈夫曼樹又稱最優樹 二叉樹 是一類帶權路徑最短的樹。構造這種樹的演算法最早是由哈夫曼 huffman 1952年提出,這種樹在資訊檢索中很有用。結點之間的路徑長度 從乙個結點到另乙個結點之間的分支數目。樹的路徑長度 從樹的根到樹中每乙個結點的路徑長度之和。結點的帶權路徑長度 從該結點到樹根之間的路徑...