1.哈夫曼樹的基本概念 |
- 結點間的路徑和路徑長度
路徑是指從一個結點到另一個結點之間的分支序列。
路徑長度是指從一個結點到另一個結點所經過的分支數目。 - 結點的權和帶權路徑長度
在實際的應用中,給樹的每個結點賦予一個具有某種實際意義的實數,稱該實數爲這個結點的權。
在樹型結構中,把從樹根到某一結點的路徑長度與該結點的權的乘積,叫做該結點的帶權路徑長度。 - 樹的帶權路徑長度
樹的帶權路徑長度爲樹中從根到所有葉子結點的各個帶權路徑長度之和,通常記爲:
2. 構造哈夫曼樹 |
哈夫曼樹:它是由 n 個帶權葉子結點構成的所有二叉樹中帶權路徑長度最短的二叉樹。因爲這種樹最早由哈夫曼(Huffman)研究,所以稱爲哈夫曼樹,又叫最優二叉樹。
- 構造哈夫曼樹的算法步驟如下:
直觀地看,先選擇權小的,所以權小的結點被放置在樹的較深層,而權較大的離根較近,這樣自然在哈夫曼樹中權越大葉子離根越近,這樣一來,在計算樹的帶權路徑長度時,自然會具有最小帶權路徑長度,這種生成算法就是一種典型的貪心法。
3. 哈夫曼樹的類型定義 |
(2)哈夫曼樹的類型定義
用靜態三叉鏈表實現的哈夫曼樹類型定義如下:
#define N 20 /* 葉子結點的最大值。*/
#define M 2 * N - 1 /* 所有結點的最大值。*/
typedef struct
{
int weight; /* 結點的權值*/
int parent; /* 雙親的下標*/
int LChild; /* 左孩子結點的下標*/
int RChild; /* 右孩子結點的下標*/
} HTNode, HuffmanTree[M + 1]; /* HuffmanTree 是一個結構數組類型,0 號單元不 用。 */
4.哈夫曼樹的算法實現 |
void CrtHuffmanTree(HuffmanTree ht, int w[], int n)
{ /*構造哈夫曼樹 ht[M+1], w[ ]存放 n 個權值。*/
for (i = 1; i <= n; i++)
ht[i] = {w[i], 0, 0, 0}; /* 1 ~ n 號單元存放葉子結點,初始化*/
m = 2 * n - 1;
for (i = n + 1; i <= m; i++)
ht[i] = {0, 0, 0, 0}; /* n+1 ~ m 號單元存放非葉結點,初始化 */
/*————————————初始化完畢!對應算法步驟 1—————————*/
for (i = n + 1; i <= m; i++) /*創建非葉結點,建哈夫曼樹*/
{
select(ht, i - 1, s1, s2); /* 在 ht[1] ~ ht[i-1] 的範圍內選擇兩個 parent 爲 0 且
weight 最小的結點,其序號分別賦值給 s1、s2 返回 */
ht[i].weight = ht[s1].weight + ht[s2].weight;
ht[s1].parent = i;
ht[s2].parent = i;
ht[i].LChild = s1;
ht[i].RChild = s2;
} /*哈夫曼樹建立完畢*/
}
該算法分成兩大部分,其中第一部分是初始化,先初始化 ht 的前 1~n 號元素,存放葉子結點(相當初始森林),它們都沒有雙親與孩子。再初始化 ht 的後 n-1 個(從 n+1~2n-1)非葉結點元素;第二部分爲實施選擇、刪除合併 n-1 次(相當步驟(2)~(4)):選擇是從當前森林中(在森林中樹的根結點的雙親爲 0)選擇兩棵根的權值最小的樹;刪除合併是將選到的兩棵樹的根權和存入 ht 的當前最前面的空閒元素中(相當於合併樹中新結點),並置入相應的雙親與孩子的位置指示。