簡單易懂的介紹----哈夫曼樹
阿新 • • 發佈:2018-11-24
1、什麼是哈夫曼樹
哈夫曼樹:它是 n 個帶權葉子結點構成的所有二叉樹中,帶權路徑長度 WPL 最小的二叉樹(又稱最優二叉樹)。
WPL樹的帶權路徑長度:
wi表示權值
li表示樹的深度
例如:
上面三棵樹的帶權路徑長度分別是:
WPL(1) = 4*2+7*3+5*3+2*1=46
WPL(2)=7*2+5*2+2*2+4*2=36
WPL(3)=7*1+5*2+2*3+4*3=35
其中第三棵就是一棵哈夫曼樹。
2、如何構建一棵哈夫曼樹
假設有n個權值,則構造出的哈夫曼樹有n個葉子結點。 n個權值分別設為 w1、w2、…、wn,則哈夫曼樹的構造規則為:
(1)將w1、w2、…,wn看成是有n 棵樹的森林(每棵樹僅有一個結點);
(2)在森林中選出兩個根結點的權值最小的樹合併,作為一棵新樹的左、右子樹,且新樹的根結點權值為其左、右子樹根結點權 值之和;
(3)從森林中刪除選取的兩棵樹,並將新樹加入森林;
(4)重複(2)、(3)步,直到森林中只剩一棵樹為止,該樹即為所求得的哈夫曼樹。
如:對下圖中的六個帶權葉子結點來構造一棵哈夫曼樹,步驟如下:
注意:為了使得到的哈夫曼樹的結構儘量唯一,通常規定生成的哈夫曼樹中每個結點的左子樹根結點的權小於等於右子樹根結點的權。
演算法如下:
//2、根據陣列 a 中 n 個權值建立一棵哈夫曼樹,返回樹根指標 struct BTreeNode* CreateHuffman(ElemType a[], int n) { int i, j; struct BTreeNode **b, *q; b = malloc(n*sizeof(struct BTreeNode)); for (i = 0; i < n; i++) //初始化b指標陣列,使每個指標元素指向a陣列中對應的元素結點 { b[i] = malloc(sizeof(struct BTreeNode)); b[i]->data = a[i]; b[i]->left = b[i]->right = NULL; } for (i = 1; i < n; i++)//進行 n-1 次迴圈建立哈夫曼樹 { //k1表示森林中具有最小權值的樹根結點的下標,k2為次最小的下標 int k1 = -1, k2; for (j = 0; j < n; j++)//讓k1初始指向森林中第一棵樹,k2指向第二棵 { if (b[j] != NULL && k1 == -1) { k1 = j; continue; } if (b[j] != NULL) { k2 = j; break; } } for (j = k2; j < n; j++)//從當前森林中求出最小權值樹和次最小 { if (b[j] != NULL) { if (b[j]->data < b[k1]->data) { k2 = k1; k1 = j; } else if (b[j]->data < b[k2]->data) k2 = j; } } //由最小權值樹和次最小權值樹建立一棵新樹,q指向樹根結點 q = malloc(sizeof(struct BTreeNode)); q->data = b[k1]->data + b[k2]->data; q->left = b[k1]; q->right = b[k2]; b[k1] = q;//將指向新樹的指標賦給b指標陣列中k1位置 b[k2] = NULL;//k2位置為空 } free(b); //刪除動態建立的陣列b return q; //返回整個哈夫曼樹的樹根指標 }
3、哈夫曼編碼
在電報通訊中,電文是以二進位制的0、1序列傳送的,每個字元對應一個二進位制編碼,為了縮短電文的總長度,採用不等長編碼方式,構造哈夫曼樹,
將每個字元的出現頻率作為字元結點的權值賦予葉子結點,每個分支結點的左右分支分別用0和1編碼,從樹根結點到每個葉子結點的路徑上
所經分支的0、1編碼序列等於該葉子結點的二進位制編碼。如上文所示的哈夫曼編碼如下:
a 的編碼為:00
b 的編碼為:01
c 的編碼為:100
d 的編碼為:1010
e 的編碼為:1011
f 的編碼為:11