瞭解赫夫曼樹一文就夠了

前言

這是我聽老師講課做的筆記。
作者:RodmaChen
關注我的csdn博客,更多數據結構與算法知識還在更新

一. 定義

  1. 路徑:從樹中一個結點到另一個結點之間的分支構成兩個結點之間
    的路徑。

  2. 路徑長度:路徑的上分支的數目。就是從一個結點到另一個結點有多少個路徑

  3. 結點的路徑長度:從到該結點的路徑長度。

  4. 樹的路徑長度:從樹根到每一個結點的路徑長度之和。

  5. 結點的權:在一些應用中,賦予樹中結點的一個有某種意義實數

  6. 結點的帶權路徑長度:從根結點到各個葉結點的路徑長度與相應結點權值的乘積。

  7. 樹的帶權路徑長度:所有葉結點的帶權路徑長度之和,記作:
    WPL=k=1nWklknWkklkk{WPL= \sum_{k=1}^n W_kl_k} (其中,n:葉結點數目;W_k:葉結點 k的權值;l_k根到k之間的路徑長度)

  8. 最優二叉樹/ 赫 夫曼樹: 假設有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

由於每個結點同時還包含其雙親信息和孩子結點的信息,所以構成
一個靜態三叉鏈表。

​ 權值 雙親 左孩子 右孩子

image-20200524000607649

(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
轉載說明:跟我說明,務必註明來源,附帶本人博客連接。

請給我點個贊鼓勵我吧
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章