哈夫曼樹詳細講解(帶例題和C語言程式碼實現——全註釋)
阿新 • • 發佈:2018-12-02
**
哈夫曼樹詳細講解(帶例題和C語言程式碼實現——全註釋)
**
- 定義
哈夫曼樹又稱最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的葉結點的權值乘上其到根結點的 路徑長度(若根結點為0層,葉結點到根結點的路徑長度為葉結點的層數)
- 計算公式
樹的帶權路徑長度記為WPL=(W1L1+W2L2+W3L3+…+WnLn) ,N個權值Wi(i=1,2,…n)構成一棵有N個葉結點的二叉樹,相應的葉結點的路徑長度為Li(i=1,2,…n)。 可以證明哈夫曼樹的WPL是最小的
- 運算過程
一、對給定的n個權值{W1,W2,W3,…,Wi,…,Wn}構成n棵二叉樹的初始集合F= {T1,T2,T3,…,Ti,…,Tn},其中每棵二叉樹Ti中只有一個權值為Wi的根結點,它的左右子樹均為空。(為方便在計算機上實現算 法,一般還要求以Ti的權值Wi的升序排列。)
二、在F中選取兩棵根結點權值最小的樹作為新構造的二叉樹的左右子樹,新二叉樹的根結點的權值為其左右子樹的根結點的權值之和。
三、從F中刪除這兩棵樹,並把這棵新的二叉樹同樣以升序排列加入到集合F中。
四、重複二和三兩步,直到集合F中只有一棵二叉樹為止
- 圖解過程
- 例題講解(重點)
如例:已知某通訊系統在通訊聯絡中只可能出現8中字元,其概率分別別是0.05, 0.29, 0.07, 0.08, 0.14, 0.23, 0.03, 0.11, 試設計赫夫曼編碼
根據所佔比例列出權值解答
注:A是0111
一共有八個節點,則陣列中需要構建的節點 m = 2*n – 1 則為15個 給八行生成樹,餘下的記錄過程
最終結果:
C語言實現程式碼
(記錄人的姓名,成績,按照成績作為權值編碼將上文中字母作為名字,比例作為成績(權值))
#include <stdio.h> #include <stdlib.h> #include <string.h> #define n 6//葉子結點數目 #define m (2*n-1) //總結點數目,可證明 #define MAXVALUE 10000 //最大權值 #define MAXBIT 20 //哈夫曼編碼最大長度 typedef struct{ char name[5]; int weight; int parent; int Lchild,Rchild; }Htreetype; typedef struct{ int bit[n];//儲存位串 int start;//編碼起始位置 char name[5]; }Hcodetype; void select(Htreetype tree[],int position,int *node1,int *node2);//選擇兩個權值最小的點(代價最小) void HuffmanTree(Htreetype t[]);//構造哈夫曼樹,用到了select選擇節點 void HuffmanCode(Hcodetype code[],Htreetype tree[]);//開始編碼 void show(Htreetype tree[],Hcodetype code[]);//顯示編碼 int main(){ Htreetype tree[m];//有m個位置的空樹 Hcodetype code[n];//n個編碼位置 HuffmanCode(code,tree);//編碼完成 show(tree,code);//輸出 return 0; } void select(Htreetype tree[],int position,int *node1,int *node2){ //tree是哈夫曼樹 position是到 第幾個節點之前的都要檢索 node1 和node2 儲存兩個權值最小的節點的位置 //一共n個待記錄點,n -- m裡面的是生成樹的時候新根節點,裡面放的是權值,當生成了新根節點,查詢最小就要從頭到新構成的位置 *node1 = *node2 = 0; int min1,min2; min1 = min2 = MAXVALUE; int i; for(i = 0; i < position; i++){ if(tree[i].parent == -1){//如果沒有父節點說明還沒有成為樹的一部分 if(tree[i].weight < min1){//維護min1 < min2 也就是 node1的權值小 min2 = min1;//min2記錄min1的權值 min1 = tree[i].weight;//min1的值更新 *node2 = *node1;//拋棄當前兩個節點中大的那個重新記錄 *node1 = i; }else if(tree[i].weight < min2){//前面的min1沒有這個小但是min2比這個小所以直接用min2記錄,這樣就不用對兩個節點都比較之後才換節點 min2 = tree[i].weight; *node2 = i; } } } } void HuffmanTree(Htreetype tree[]){ int i;//for迴圈的計數器 int node1,node2;//找權值最小的兩個節點位置 node1 = node2 = 0; char name[5];//暫存姓名 int now_weight;//暫存權值 for(i = 0; i < m; i++){//一共n個節點,則需要構建m個點儲存資訊 m = (2*n - 1) tree[i].weight = 0;//沒有權值 tree[i].parent = -1; tree[i].Lchild = -1; tree[i].Rchild = -1; } printf("一共有%d個人\n",n);//構造節點資訊 for(i = 0; i < n; i++){//輸入基本資訊 printf("請輸入姓名:"); scanf("%s",&name); printf("請輸入成績:"); scanf("%d",&now_weight); strcpy(tree[i].name,name); tree[i].weight = now_weight; } for(i = n; i < m; i++){//構造哈夫曼樹 select(tree,i,&node1,&node2);//選好兩個節點 tree[node1].parent = i;//選好的兩個節點形成了樹 tree[node2].parent = i;//生成的節點放在當前位置 tree[i].Lchild = node1;//node1權值小,也就是左子樹權值都小 tree[i].Rchild = node2; tree[i].weight = tree[node1].weight + tree[node2].weight;//生成新樹完成 } } void HuffmanCode(Hcodetype code[],Htreetype tree[]){//code存放每個節點的編碼對應0 --- (n - 1)個節點編碼 int i;//計數器 int parent_position,now_position; Hcodetype cd;//暫時存放編碼 HuffmanTree(tree);//構造好霍夫曼樹了在 tree裡面 for(i = 0; i < n; i++){//給n個節點編碼 cd.start = n;//最長為n strcpy(cd.name,tree[i].name);//將名字也放進裡面,可省略換其餘方式輸出 now_position = i;//從每個節點向上 parent_position = tree[i].parent;//找到對應的父節點看自己當前編碼為0還是1 while(parent_position != -1){//沒有到最底層的樹 cd.start--; if(tree[parent_position].Lchild == now_position){//當前位置的是父節點的左孩子則為0 cd.bit[cd.start] = '0'; }else cd.bit[cd.start] = '1'; now_position = parent_position;//向上移動 一定要移動 parent_position = tree[now_position].parent; } code[i] = cd;//對第i個節點編碼完成後放進code中 } } void show(Htreetype tree[],Hcodetype code[]){//顯示編碼 int i,j;//計數器 for(i = 0; i < n; i++){ printf("%s ",code[i].name); for(j = code[i].start; j < n; j++) printf("%c ",code[i].bit[j]); printf("\n"); } }
輸出結果
與貼圖不同的原因在於此程式碼將同級較小權值的字母放在了左面