學習目標

  • 理解霍夫曼編碼的原理和應用
  • 掌握霍夫曼樹的建構過程
  • 能夠根據字元頻率建立霍夫曼樹並產生編碼
  • 理解霍夫曼編碼的優點和特點

1. 資料壓縮的基本概念

1.1 為什麼需要資料壓縮?

  • 節省儲存空間: 減少檔案大小
  • 節省傳輸時間: 減少網路傳輸時間
  • 節省頻寬: 減少網路頻寬使用

1.2 編碼方式

固定長度編碼: 每個字元使用相同位元數 - 例如: ASCII 碼,每個字元 8 位元

可變長度編碼: 不同字元使用不同位元數 - 常用字元用較短編碼 - 不常用字元用較長編碼 - 目標: 總編碼長度最小

1.3 霍夫曼編碼

霍夫曼編碼 (Huffman Coding) 是一種無損資料壓縮演算法,根據字元出現的頻率,為每個字元分配一個可變長度的二進位編碼。

核心思想: 出現頻率高的字元使用較短的編碼,出現頻率低的字元使用較長的編碼。

2. 霍夫曼編碼的原理

2.1 基本步驟

  1. 統計頻率: 計算每個字元出現的頻率
  2. 建立節點: 為每個字元建立一個樹節點
  3. 建構霍夫曼樹: 重複合併頻率最小的兩個節點
  4. 產生編碼: 從根節點到葉節點的路徑即為編碼
  5. 編碼資料: 使用產生的編碼替換原始字元

2.2 霍夫曼樹的性質

  • 二元樹: 每個節點最多有兩個子節點
  • 葉節點: 代表原始字元
  • 內部節點: 代表合併的節點,頻率為子節點頻率之和
  • 前綴碼: 沒有任何編碼是另一個編碼的前綴(保證解碼唯一性)

3. 建構霍夫曼樹的詳細過程

3.1 範例 1: 基本範例

字元頻率: A(11), B(8), C(9), D(20), E(31), F(14), G(10)

步驟 1: 建立初始節點

將每個字元及其頻率視為一個獨立的樹節點(葉節點):

A(11)  B(8)  C(9)  D(20)  E(31)  F(14)  G(10)

步驟 2: 第一次合併

選擇頻率最小的兩個節點:B(8) 和 C(9)

建立父節點,頻率 = 8 + 9 = 17

      (17)
     /    \
   B(8)  C(9)

剩餘節點:A(11), (17), D(20), E(31), F(14), G(10)

步驟 3: 第二次合併

選擇頻率最小的兩個:A(11) 和 G(10)

建立父節點,頻率 = 11 + 10 = 21

      (21)           (17)
     /    \         /    \
   A(11) G(10)    B(8)  C(9)

剩餘節點:(21), (17), D(20), E(31), F(14)

步驟 4: 第三次合併

選擇頻率最小的兩個:(17) 和 F(14)

建立父節點,頻率 = 17 + 14 = 31

      (31)              (21)
     /    \            /    \
   (17)  F(14)      A(11) G(10)
  /    \
B(8)  C(9)

剩餘節點:(31), (21), D(20), E(31)

步驟 5: 第四次合併

選擇頻率最小的兩個:(21) 和 D(20)

建立父節點,頻率 = 21 + 20 = 41

      (41)              (31)
     /    \            /    \
   (21)  D(20)      (17)  F(14)
  /    \           /    \
A(11) G(10)     B(8)  C(9)

剩餘節點:(41), (31), E(31)

步驟 6: 第五次合併

選擇頻率最小的兩個:(31) 和 E(31)

建立父節點,頻率 = 31 + 31 = 62

      (62)              (41)
     /    \            /    \
   (31)  E(31)      (21)  D(20)
  /    \           /    \
(17)  F(14)    A(11) G(10)
/    \
B(8)  C(9)

剩餘節點:(62), (41)

步驟 7: 最後合併

合併最後兩個節點:(62) 和 (41)

建立根節點,頻率 = 62 + 41 = 103

        (103)
       /      \
     (62)      (41)
    /    \    /    \
  (31)  E(31)(21)  D(20)
 /    \     /    \
(17) F(14) A(11) G(10)
/    \
B(8)  C(9)

步驟 8: 標示邊緣並產生編碼

從根節點開始,為所有左分支標示為 0,右分支標示為 1:

         (103)
       0/      \1
     (62)      (41)
   0/    \1   0/    \1
  (31)  E(31)(21)  D(20)
 0/  \1   0/    \1
(17)F(14)A(11)G(10)
0/  \1
B(8) C(9)

步驟 9: 產生編碼表

從根節點到每個葉節點的路徑:

字元 頻率 路徑 編碼
E 31 1
D 20 右右 11
A 11 右左右 101
G 10 右右右 111
F 14 右左左 100
B 8 左右左左 0100
C 9 左右左右 0101

3.2 範例 2: 等頻率字元

字元頻率: A(1), B(1), C(1), D(1), E(1), F(1), G(1), H(1), I(1), J(1)

當所有字元頻率相同時,會建立一個平衡的二元樹。

建構過程(簡化)

由於頻率都相同,可以任意選擇合併順序。最終會得到一個接近平衡的樹。

可能的編碼結果:

字元 編碼 字元 編碼
A 0000 F 1000
B 0001 G 1001
C 0010 H 1010
D 0011 I 1011
E 0100 J 11

注意: 當頻率相同時,編碼長度可能不完全相同,但總長度會接近最優。

4. 編碼與解碼

4.1 編碼過程

使用編碼表將原始字串轉換為二進位編碼。

範例: 使用範例 1 的編碼表編碼字串 "EDD"

  • E → 1
  • D → 11
  • D → 11

編碼結果: 11111

4.2 解碼過程

從左到右讀取編碼,根據霍夫曼樹進行解碼。

範例: 解碼 11111

  1. 讀取 1 → 對應到 E
  2. 讀取 11 → 對應到 D
  3. 讀取 11 → 對應到 D

解碼結果: "EDD"

注意: 由於霍夫曼編碼是前綴碼,解碼過程是唯一的。

5. 霍夫曼編碼的優點

5.1 最優前綴碼

在給定字元頻率的情況下,霍夫曼編碼產生的編碼長度總和是最小的(或接近最小)。

5.2 無損壓縮

解碼後可以完全還原原始資料,不會有任何損失。

5.3 適應性

可以根據實際資料的頻率分佈調整編碼,對不同類型的資料都有良好的壓縮效果。

5.4 前綴碼特性

沒有任何編碼是另一個編碼的前綴,保證了解碼的唯一性和正確性。

6. 霍夫曼編碼的缺點

6.1 需要頻率資訊

在編碼前需要先統計字元頻率,需要兩次掃描資料。

6.2 編碼表需要傳輸

解碼時需要知道編碼表,編碼表本身也需要儲存或傳輸。

6.3 對小資料集效果有限

如果資料量很小,編碼表的開銷可能大於壓縮節省的空間。

7. 複雜度分析

7.1 時間複雜度

  • 建構霍夫曼樹: O(n log n),其中 n 是不同字元的數量
  • 編碼: O(m),其中 m 是原始資料的長度
  • 解碼: O(m)

7.2 空間複雜度

  • 儲存霍夫曼樹: O(n)
  • 編碼表: O(n)

8. 實際應用

8.1 檔案壓縮

  • ZIP 檔案格式
  • GZIP 壓縮
  • 許多壓縮工具都使用霍夫曼編碼或其變體

8.2 影像壓縮

  • JPEG 影像格式使用霍夫曼編碼
  • 用於壓縮 DCT 係數

8.3 通訊協定

  • 網路傳輸中的資料壓縮
  • 減少傳輸時間和頻寬使用

9. 練習題

練習題 1

根據以下字元頻率建立霍夫曼樹並產生編碼: A(5), B(2), C(3), D(7), E(1)

解答:

步驟 1: 初始節點

A(5)  B(2)  C(3)  D(7)  E(1)

步驟 2: 合併 E(1) 和 B(2) → (3)

(3)  A(5)  C(3)  D(7)
/  \
E(1) B(2)

步驟 3: 合併 (3) 和 C(3) → (6)

(6)      A(5)  D(7)
/    \
(3)  C(3)
/  \
E(1) B(2)

步驟 4: 合併 A(5) 和 (6) → (11)

(11)         D(7)
/    \
A(5)  (6)
     /    \
   (3)  C(3)
  /  \
E(1) B(2)

步驟 5: 合併 (11) 和 D(7) → (18) [根節點]

標示邊緣(左=0, 右=1):

字元 頻率 編碼
D 7 0
A 5 10
C 3 111
B 2 1101
E 1 1100

練習題 2

使用練習題 1 的編碼表編碼字串 "DACB"。

解答: - D → 0 - A → 10 - C → 111 - B → 1101

編碼結果: 0101111101

練習題 3

為什麼霍夫曼編碼是前綴碼?這有什麼好處?

解答: 霍夫曼編碼是前綴碼,因為每個字元的編碼對應到霍夫曼樹的一個葉節點,而從根到葉的路徑是唯一的。沒有任何編碼是另一個編碼的前綴,因為如果一個編碼是另一個的前綴,那麼在樹中一個節點會是另一個節點的祖先,但葉節點不能是其他葉節點的祖先。

好處: 1. 解碼唯一性: 可以從左到右逐位解碼,不需要回溯 2. 解碼效率: 解碼過程簡單快速 3. 即時解碼: 可以邊接收邊解碼,不需要等待完整編碼

10. 考試重點提醒

  1. 建構過程: 要能清楚地展示每一步的合併過程
  2. 編碼產生: 理解如何從樹的路徑產生編碼
  3. 前綴碼: 理解為什麼霍夫曼編碼是前綴碼
  4. 頻率統計: 記住要先統計頻率
  5. 編碼表: 要能以表格形式清晰呈現編碼結果