一、哈夫曼樹的概念和定義
什麼是哈夫曼樹?
讓我們先舉一個例子。
判定樹:
- if(score<60)
- cout<<"Bad"<<endl;
- else if(score<70)
- cout<<"Pass"<<endl
- else if(score<80)
- cout<<"General"<<endl;
- else if(score<90)
- cout<<"Good"<<endl;
- else
- cout<<"Very good!"<<endl;
定義哈夫曼樹之前先說明幾個與哈夫曼樹有關的概念:
路徑: 樹中一個結點到另一個結點之間的分支構成這兩個結點之間的路徑。
路徑長度:路徑上的分枝數目稱作路徑長度。
樹的路徑長度:從樹根到每一個結點的路徑長度之和。
結點的帶權路徑長度:在一棵樹中,如果其結點上附帶有一個權值,通常把該結點的路徑長度與該結點上的權值
之積稱爲該結點的帶權路徑長度(weighted path length)
什麼是權值?( From 百度百科 )
計算機領域中(數據結構)
權值就是定義的路徑上面的值。可以這樣理解爲節點間的距離。通常指字符對應的二進制編碼出現的概率。
至於霍夫曼樹中的權值可以理解爲:權值大表明出現概率大!
一個結點的權值實際上就是這個結點子樹在整個樹中所佔的比例.
abcd四個葉子結點的權值爲7,5,2,4. 這個7,5,2,4是根據實際情況得到的,比如說從一段文本中統計出abcd四個字母出現的次數分別爲7,5,2,4. 說a結點的權值爲7,意思是說a結點在系統中佔有7這個份量.實際上也可以化爲百分比來表示,但反而麻煩,實際上是一樣的.
樹的帶權路徑長度:如果樹中每個葉子上都帶有一個權值,則把樹中所有葉子的帶權路徑長度之和稱爲樹的帶
權路徑長度。
設某二叉樹有n個帶權值的葉子結點,則該二叉樹的帶權路徑長度記爲:
公式中,Wk爲第k個葉子結點的權值;Lk爲該結點的路徑長度。
示例:
二、哈夫曼樹的構造
三、哈夫曼樹的在編碼中的應用
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- using namespace std;
- #define N 10 // 帶編碼字符的個數,即樹中葉結點的最大個數
- #define M (2*N-1) // 樹中總的結點數目
- class HTNode{ // 樹中結點的結構
- public:
- unsigned int weight;
- unsigned int parent,lchild,rchild;
- };
- class HTCode{
- public:
- char data; // 待編碼的字符
- int weight; // 字符的權值
- char code[N]; // 字符的編碼
- };
- void Init(HTCode hc[], int *n){
- // 初始化,讀入待編碼字符的個數n,從鍵盤輸入n個字符和n個權值
- int i;
- printf("input n = ");
- scanf("%d",&(*n));
- printf("\ninput %d character\n",*n);
- fflush(stdin);
- for(i=1; i<=*n; ++i)
- scanf("%c",&hc[i].data);
- printf("\ninput %d weight\n",*n);
- for(i=1; i<=*n; ++i)
- scanf("%d",&(hc[i].weight) );
- fflush(stdin);
- }//
- void Select(HTNode ht[], int k, int *s1, int *s2){
- // ht[1...k]中選擇parent爲0,並且weight最小的兩個結點,其序號由指針變量s1,s2指示
- int i;
- for(i=1; i<=k && ht[i].parent != 0; ++i){
- ; ;
- }
- *s1 = i;
- for(i=1; i<=k; ++i){
- if(ht[i].parent==0 && ht[i].weight<ht[*s1].weight)
- *s1 = i;
- }
- for(i=1; i<=k; ++i){
- if(ht[i].parent==0 && i!=*s1)
- break;
- }
- *s2 = i;
- for(i=1; i<=k; ++i){
- if(ht[i].parent==0 && i!=*s1 && ht[i].weight<ht[*s2].weight)
- *s2 = i;
- }
- }
- void HuffmanCoding(HTNode ht[],HTCode hc[],int n){
- // 構造Huffman樹ht,並求出n個字符的編碼
- char cd[N];
- int i,j,m,c,f,s1,s2,start;
- m = 2*n-1;
- for(i=1; i<=m; ++i){
- if(i <= n)
- ht[i].weight = hc[i].weight;
- else
- ht[i].parent = 0;
- ht[i].parent = ht[i].lchild = ht[i].rchild = 0;
- }
- for(i=n+1; i<=m; ++i){
- Select(ht, i-1, &s1, &s2);
- ht[s1].parent = i;
- ht[s2].parent = i;
- ht[i].lchild = s1;
- ht[i].rchild = s2;
- ht[i].weight = ht[s1].weight+ht[s2].weight;
- }
- cd[n-1] = '\0';
- for(i=1; i<=n; ++i){
- start = n-1;
- for(c=i,f=ht[i].parent; f; c=f,f=ht[f].parent){
- if(ht[f].lchild == c)
- cd[--start] = '0';
- else
- cd[--start] = '1';
- }
- strcpy(hc[i].code, &cd[start]);
- }
- }
- int main()
- {
- int i,m,n,w[N+1];
- HTNode ht[M+1];
- HTCode hc[N+1];
- Init(hc, &n); // 初始化
- HuffmanCoding(ht,hc,n); // 構造Huffman樹,並形成字符的編碼
- for(i=1; i<=n; ++i)
- printf("\n%c---%s",hc[i].data,hc[i].code);
- printf("\n");
- return 0;
- }