一、相關概念
1、葉子結點的權值(weight)是對葉子結點賦予的一個有意義的數值量。
2、設二叉樹有n個帶權值的葉子結點,從根節點到各個葉子結點的路徑長度與相應葉子結點權值的乘積之和叫做二叉樹的帶權路徑長度。
3、給定一組具有確定權值的葉子結點,可以構造出不同的二叉樹,將其中帶權路徑長度最小的二叉樹稱為哈夫曼樹。
二、哈夫曼算法基本思想
(1) 以權值分別為W1,W2...Wn的n各結點,構成n棵二叉樹T1,T2,...Tn並組成森林F={T1,T2,...Tn},其中每棵二叉樹 Ti僅有一個權值為 Wi的根結點;
(2) 在F中選取兩棵根結點權值最小的樹作為左右子樹構造一棵新二叉樹,並且置新二叉樹根結點權值為左右子樹上根結點的權值之和(根結點的權值=左右孩子權值之和,葉結點的權值= Wi);
(3) 從F中刪除這兩棵二叉樹,同時將新二叉樹加入到F中;
(4) 重復(2)、(3)直到F中只含一棵二叉樹為止,這棵二叉樹就是Huffman樹。
三、哈夫曼算法的存儲結構
考慮到對於有n個葉子結點的哈夫曼樹有2n-1個結點,並且進行n-1次合並操作,為了便於選取根節點權值最小的二叉樹以及合並操作,設置一個數組haftree[2n-1],保存哈夫曼樹中的各個結點的信息,數組元素的結點結構如下圖所示:
weight | lchild | rchild | parent |
哈夫曼樹的結點結構
其中,weight保存結點權值;
lchild保存該節點的左孩子在數組中的下標;
rchild保存該節點的右孩子在數組中的下標;
parent保存該節點的雙親孩子在數組中的下標。
可以用C++語言中的結構體類型定義上述結點,如下:
1 // 哈夫曼樹的結點結構 2 struct element 3 { 4 int weight; // 權值域 5 int lchild, rchild, parent; // 該結點的左、右、雙親結點在數組中的下標 6 };
四、哈夫曼算法C++實現
為了判定一個結點是否已經加入哈夫曼樹中,可通過parent域的值來確定。初始時parent的值為-1,當某結點加入到樹中時,該節點parent域的值為其雙親結點在數組中的下標。
構造哈夫曼樹時,首先將n個權值的葉子結點存放到數組haftree的前n個分量中,然后不斷將兩棵子樹合並為一棵子樹,並將新子樹的根節點順序存放到數組haftree的前n個分量的后面。
哈夫曼算法用偽代碼描述為:
1、數組haftree初始化,所有數組元素的雙親、左右孩子都置為-1;
2、數組haftree的前n個元素的權值置給定權值;
3、進行n-1次合並
3.1 在二叉樹集合中選取兩個權值最小的根節點,其下標分別為i1,i2;
3.2 將二叉樹i1、i2合並為一棵新的二叉樹k。
哈夫曼算法完整代碼描述如下;
1 #include<iostream> 2 #include <iomanip>//這個頭文件是聲明一些 “流操作符”的 3 //比較常用的有:setw(int);//設置顯示寬度,left//right//設置左右對齊。 setprecision(int);//設置浮點數的精確度。 4 using namespace std; 5 // 哈夫曼樹的結點結構 6 struct element 7 { 8 int weight; // 權值域 9 int lchild, rchild, parent; // 該結點的左、右、雙親結點在數組中的下標 10 }; 11 // 選取權值最小的兩個結點 12 void selectMin(element a[],int n, int &s1, int &s2) 13 { 14 for (int i = 0; i < n; i++) 15 { 16 if (a[i].parent == -1)// 初始化s1,s1的雙親為-1 17 { 18 s1 = i; 19 break; 20 } 21 } 22 for (int i = 0; i < n; i++)// s1為權值最小的下標 23 { 24 if (a[i].parent == -1 && a[s1].weight > a[i].weight) 25 s1 = i; 26 } 27 for (int j = 0; j < n; j++) 28 { 29 if (a[j].parent == -1&&j!=s1)// 初始化s2,s2的雙親為-1 30 { 31 s2 = j; 32 break; 33 } 34 } 35 for (int j = 0; j < n; j++)// s2為另一個權值最小的結點 36 { 37 if (a[j].parent == -1 && a[s2].weight > a[j].weight&&j != s1) 38 s2 = j; 39 } 40 } 41 // 哈夫曼算法 42 // n個葉子結點的權值保存在數組w中 43 void HuffmanTree(element huftree[], int w[], int n) 44 { 45 for (int i = 0; i < 2*n-1; i++) // 初始化,所有結點均沒有雙親和孩子 46 { 47 huftree[i].parent = -1; 48 huftree[i].lchild = -1; 49 huftree[i].rchild = -1; 50 } 51 for (int i = 0; i < n; i++) // 構造只有根節點的n棵二叉樹 52 { 53 huftree[i].weight = w[i]; 54 } 55 for (int k = n; k < 2 * n - 1; k++) // n-1次合並 56 { 57 int i1, i2; 58 selectMin(huftree, k, i1, i2); // 查找權值最小的倆個根節點,下標為i1,i2 59 // 將i1,i2合並,且i1和i2的雙親為k 60 huftree[i1].parent = k; 61 huftree[i2].parent = k; 62 huftree[k].lchild = i1; 63 huftree[k].rchild = i2; 64 huftree[k].weight = huftree[i1].weight + huftree[i2].weight; 65 } 66 67 }
// 打印哈夫曼樹 68 void print(element hT[],int n) 69 { 70 cout << "index weight parent lChild rChild" << endl; 71 cout << left; // 左對齊輸出 72 for (int i = 0; i < n; ++i) 73 { 74 cout << setw(5) << i << " "; 75 cout << setw(6) << hT[i].weight << " "; 76 cout << setw(6) << hT[i].parent << " "; 77 cout << setw(6) << hT[i].lchild << " "; 78 cout << setw(6) << hT[i].rchild << endl; 79 } 80 } 81 int main() 82 { 83 int x[] = { 5,29,7,8,14,23,3,11 }; // 權值集合 84 element *hufftree=new element[2*8-1]; // 動態創建數組 85 HuffmanTree(hufftree, x, 8); 86 print(hufftree,15); 87 system("pause"); 88 return 0; 89 }
最后的輸出結果為:
參考文獻:
[1]王紅梅, 胡明, 王濤. 數據結構(C++版)[M]. 北京:清華大學出版社。
2018-01-03