前言
這是我聽老師講課做的筆記。
作者:RodmaChen
關注我的csdn博客,更多數據結構與算法知識還在更新
一. 定義
-
路徑:從樹中一個結點到另一個結點之間的分支構成兩個結點之間
的路徑。 -
路徑長度:路徑的上分支的數目。就是從一個結點到另一個結點有多少個路徑
-
結點的路徑長度:從根到該結點的路徑長度。
-
樹的路徑長度:從樹根到每一個結點的路徑長度之和。
-
結點的權:在一些應用中,賦予樹中結點的一個有某種意義實數
-
結點的帶權路徑長度:從根結點到各個葉結點的路徑長度與相應結點權值的乘積。
-
樹的帶權路徑長度:所有葉結點的帶權路徑長度之和,記作:
-
最優二叉樹/ 赫 夫曼樹: 假設有n個權值(w1,w2…wn),試
構造一棵有n個葉子結點的二叉樹,每個葉子結點帶權爲wi ,
則其中帶權路徑長度WPL最小的二叉樹稱作最優二叉樹或赫
夫曼樹。
二. 赫夫曼算法
1.基本思想
給定n個權值,先找出兩個最小的權值
wi,wj,求wi+wj=wk
,把wk
與其它權值繼續重複此動作,直至所有的wi
都成爲葉子結點。
如下圖所示:給定7,5,3,1
。選取根結點的權值最小(1)和次小(3)的兩棵二叉樹作爲新的二叉樹的左右子樹構造新的二叉樹,新的二叉樹的根結點權值爲左右子樹根結點權值之和(4)。最後7,5,3,1
變成葉子結點。這樣一顆樹就是赫夫曼樹。
2.算法的實現
(1)赫夫曼樹的存儲結構:
赫夫曼樹是一種二叉樹,由於赫夫曼樹中沒有度爲 1
的結點,因此一棵有 n
個葉子的赫夫曼樹共有 2n-1
個結點,可以用一個大小爲 2n-1
的一維數組存放赫夫曼樹的各個結點。
爲什麼是2n-1: [根據二叉樹的性質三](file:///D:/課本筆記/我的筆記/計算機數據結構與算法筆記/第六章:樹和二叉樹的性質和定義.html)由於沒有度爲1的結點,所以度爲2的結點N=n-1,總結點等於2n-1
由於每個結點同時還包含其雙親信息和孩子結點的信息,所以構成
一個靜態三叉鏈表。
權值 雙親 左孩子 右孩子
(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 號單元不用。 */
void CrtHuffmanTree(HuffmanTree ht, int w[ ], int n)
{ /*構造哈夫曼樹 ht[M+1], w[ ]存放 n 個權值。*/
/* 1 ~ n 號單元存放葉子結點,初始化*/
for(i=1;i<=n;i++) ht[i] ={ w[i],0,0,0};
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;
} /*哈夫曼樹建立完畢*/
}
(3)代碼事列:
數據傳送中的二進制編碼。要傳送數據
state, seat,act, tea, cat, set, a,eat
,如何使傳送的長度最短?爲了保證長度最短,先計算各個字符出現次數,將出現次數當作權值,如下表所示。
按照創建哈夫曼樹算法,該例建立哈夫曼樹的結果如下表所示:
三.赫夫曼編碼
1.編碼和解碼(考例題)
(1)數據壓縮過程稱爲編碼。即將文件中的每個字符均轉
換爲一個惟一的二進制位串。
(2)數據解壓過程稱爲解碼。即將二進制位串轉換爲對應
的字符。
等長編碼:每個字符的碼長一樣。
變長編碼:頻度高的(出現多的)字符編碼短,反之則長。
舉例: 假設需傳送的電文爲’ABACCDA'
,分別按定長和不定長編碼,如
下表所示
如上表所示:A、B、C、D的定長編碼分別爲00、01、10和11
,則上
述7個字符的電文便爲’00010010101100’
,總長14位,對方接收時,可
按二位一分進行譯碼。
如果設計A、B、C、D的編碼分別爲0、00、1和01
,則上述7個字符的
電文可轉換成總長爲9的字符串’000011010'
。但是,這樣的電文無法翻
譯,例如傳送過去的字符串中前4個字符的子串’0000’就可有多種譯法,
或是’AAAA'
,或是’ABA’
,也可以是’BB’
等。
前面兩種編碼一個太長,一個容易出錯
前綴編碼:若要設計長短不等的編碼,則必須是任一個字符的編碼
都不是另一個字符的編碼的前綴
舉例:可以利用二叉樹來設計二進制的前綴編碼。左走0右走1
二進制前綴 編碼便稱爲赫夫曼編碼。
2.編碼實現
由於赫夫曼樹中沒有度爲1的結點,則一棵有
n
個葉子結點的赫夫曼樹
共有2n-1
個結點,可以存儲在一個大小爲2n-1
的一維數組中。如何選定結點結構?由於在構成赫夫曼樹之後,爲求編碼需從葉子結點出發走一條從葉子到根的路徑;而爲譯碼需從根出發走一條從根到葉子的路徑。則對每個結點而言,既需知雙親的信息,又需知孩子結點的信息。
由此,設定下述存儲結構:
//一一一一赫夫曼樹和赫夫曼編碼的存儲表示----------
typedef struct{
unsigned int weight;
unsigned int parent, lchild, rchild;
}EINode, * HuffmanTree: //動態分配數組存儲赫夫曼樹
typedef char * *HuffmanCode; //動態分配數組存儲赫夫曼編碼表
void HaffmanCoding(HuffmanTree &HT, HuffmanCode &HC,int *w,int n) {
// w存放n個字符的權值(均>0),構造赫夫曼樹HT,並求出n個字符的赫夫曼編碼HC
/---構造赫夫曼樹HT---
if (n<= 1) return;//如果只有一個結點,就沒必要構造了
m = 2 * n - 1; //結點總數
HT = (HuffmanTree)malloc((m+ 1) * sizeof(HTNode)); //0號單元未用
for (p=HT, i=1; i<=n; ++i, ++p, ++w) *p = {*w,0, 0,0 );
for(; i≤=m; ++i,++p) *p ={0,0,0.0};
for (i=n+ 1; i<=m; ++i) { //建赫夫曼樹
//在HT[1..i-1]選擇parent 爲0且weight 最小的兩個結點,其序號分別爲s1s2.
Select(HT, i- 1, sl, s2);
HT[sl]. parent = i; HT[s2]. parent = i;
HT[i]. Ichild = s1; HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
/---從葉子到根逆向求每個字符的赫夫曼編碼---
HC= (HuffmanCode)malloc ((n+1)*sizeof(char *)); //分配n個字符編碼的頭指針向量
cd = (char * )malloc(n* sizeof(char)); //分配求編碼的工作空間
cd[n-1] = "\0"; //編碼結束符。
for(i=1; i<=n; ++i) { //逐個字符求赫夫曼編碼
start = n-1; //編碼結束符位置
for (c=i, f= HT[i]. parent; f!=0; :c= f, f=HT[f].parent) //從葉子到根逆向求編碼
if (HT[f].lchild==c) cd[--start] = "0”;
else cd[--start] ="1";
HC[i] = (char *)malloc((n-start)*sizeof(char)); // 爲第i個字符編碼分配空間
strcpy(HC[i], &cd[start]); //從cd複製編碼(串)到HC
}
free(ed); //釋放工作空間
}//HuffanCoding
臨時存儲編碼(倒着存)
四.邊學邊練
(1)分析這三棵樹路徑長度和帶權路徑長度那個最小有什麼特點。
(a):WPL=72+52+22+42=36 樹的路徑長度:10
(b):WPL= 73+53+21+42=46 樹的路徑長度:12
©: WPL= 71+52+23+43=35 樹的路徑長度:12
(2) 給定8個權值:5,29,7,8,14,23,3,11,構造赫夫曼樹
答案:
例6-2 已知某系統在通信聯絡中只可能出現8種字符,其概率分別爲0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11
,試設計赫夫曼編碼。
解答:設權W=(5,29,7,8,14,23,3,11),n=8,則m=15,按上述算法可構造-棵赫夫曼樹如圖6.26所示。其存儲結構HT的初始狀態如圖6.27(a)所示,其終結狀態如圖6.276)所示,所得赫夫曼編碼如圖6.27©所示。
作者:RodmaChen
本人博客:https://blog.csdn.net/weixin_46654114
本人b站求關注:https://space.bilibili.com/391105864
轉載說明:跟我說明,務必註明來源,附帶本人博客連接。
請給我點個贊鼓勵我吧