字母 頻率 編碼 space 7 111 a 4 010 e 4 000 f 3 1101 h 2 1010 i 2 1000 m 2 0111 n 2 0010 s 2 1011 t 2 0110 l 1 11001 o 1 00110 p 1 10011 r 1 11000 u 1 00111 x 1 10010 霍夫曼編碼，又譯為哈夫曼編碼、赫夫曼編碼，是一種用於無損資料壓縮的熵編碼（權編碼）演演算法。由美國電腦科學家大衛霍夫曼於 1952 年發明。 簡介 在計算機資訊處理中，霍夫曼編碼使用變長編碼表對源符號（如檔案中的一個字母）進行編碼，其中變長編碼表是透過一種評估來源符號出現機率的方法得到的，出現機率高的字母使用較短的編碼，反之出現機率低的則使用較長的編碼，這便使編碼之後的字串的平均長度、期望值降低，從而達到無失真壓縮資料的目的。 例如，在英文中，e 的出現機率最高，而 z 的出現機率則最低。當利用霍夫曼編碼對一篇英文文章進行壓縮時，e 極有可能用一個位元來表示，而 z 則可能花去 25 個位元（不是 26）。用普通的表示方法時，每個英文字母均佔用一個位元組，即 8 個位元。二者相比，e 使用了一般編碼的 18 的長度，z 則使用了 3 倍多。倘若我們能實現對於英文中各個字母出現機率的較準確的估算，就可以大幅度提高無失真壓縮的比例。 霍夫曼樹又稱最優二叉樹，是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度，就是樹中所有的葉結點的權值乘上其到根結點的路徑長度（若根結點為 0 層，葉結點到根結點的路徑長度為葉結點的層數）。樹的路徑長度是從樹根到每一結點的路徑長度之和，記為 WPL（W1L1W2L2W3L3WnLn），N 個權值 Wi（i12n）構成一棵有 N 個葉結點的二叉樹，相應的葉結點的路徑長度為 Li（i12n）。可以證明霍夫曼樹的 WPL 是最小的。 歷史 1951 年，霍夫曼在麻省理工學院攻讀博士學位，他和修讀資訊理論課程的同學得選擇是完成學期報告還是期末考試。導師羅伯特法諾出的學期報告題目是：尋找最有效的二進位制編碼。由於無法證明哪個已有編碼是最有效的，霍夫曼放棄對已有編碼的研究，轉向新的探索，最終發現了基於有序頻率二叉樹編碼的想法，並很快證明瞭這個方法是最有效的。霍夫曼使用自底向上的方法構建二叉樹，避免了次優演算法夏農 範諾編碼的最大弊端自頂向下構建樹。 1952 年，於論文《一種構建極小多餘編碼的方法》中發表了這個編碼方法。 問題定義與解法 thumb500pxFig1 thumb500pxFig3 廣義 給定 一組符號（Symbol）和其對應的權重值（weight），其權重通常表示成 zhcn 機率；zhtw 機率；。 欲知 一組二元的前置碼，其二元碼的長度為最短。 狹義 輸入 符號集合 S lefts_1s_2cdotss_nright，其 S 集合的大小為 n。 權重集合 W leftw_1w_2cdotsw_nright，其 W 集合不為負數且 w_i mathrm weightleft s_iright 1leq i leq n。 輸出 一組編碼 C left SWright leftc_1c_2cdotsc_nright，其 C 集合是一組二進位制編碼且 c_i 為 s_i 相對應的編碼，1leq i leq n。 目標 設 L left Cright sum_i1nw_itimesmathrm lengthleft c_iright 為 C 的加權的路徑長，對所有編碼 T left SWright，則 Lleft Cright leq Lleft Tright 範例 霍夫曼樹常處理符號編寫工作。根據整組資料中符號出現的頻率高低，決定如何給符號編碼。如果符號出現的頻率越高，則給符號的碼越短，相反符號的號碼越長。假設我們要給一個英文單字 F O R G E T 進行霍夫曼編碼，而每個英文字母出現的頻率分別列在 Fig1。 演算過程 （一）進行霍夫曼編碼前，我們先建立一個霍夫曼樹。 ⒈將每個英文字母依照出現頻率由小排到大，最小在左，如 Fig1。 ⒉每個字母都代表一個終端節點（葉節點），比較 FORGET 六個字母中每個字母的出現頻率，將最小的兩個字母頻率相加合成一個新的節點。如 Fig2 所示，發現 F 與 O 的頻率最小，故相加 235。 ⒊比較 5RGET，發現 R 與 G 的頻率最小，故相加 448。 ⒋比較 58ET，發現 5 與 E 的頻率最小，故相加 5510。 ⒌比較 810T，發現 8 與 T 的頻率最小，故相加 8715。 ⒍最後剩 1015，沒有可以比較的物件，相加 101525。 最後產生的樹狀圖就是霍夫曼樹，參考 Fig2。 （二）進行編碼 1 給霍夫曼樹的所有左鏈結 0 與右鏈結 1。 2 從樹根至樹葉依序記錄所有字母的編碼，如 Fig3。 實現方法 資料壓縮 實現霍夫曼編碼的方式主要是建立一個二元樹和其節點。這些樹的節點可以儲存在陣列裡，陣列的大小為符號（symbols）數的大小 n，而節點分別是終端節點（葉節點）與非終端節點（內部節點）。 一開始，所有的節點都是終端節點，節點內有三個欄位： 1 符號（Symbol） 2 權重（Weight、Probabilities、Frequency） 3 指向父節點的鏈結（Link to its parent node） 而非終端節點內有四個欄位： 1 權重（Weight、Probabilities、Frequency） 2 指向兩個子節點的 鏈結（Links to two child node） 3 指向父節點的鏈結（Link to its parent node） 基本上，我們用 0 與 1 分別代表指向左子節點與右子節點，最後為完成的二元樹共有 n 個終端節點與 n1 個非終端節點，去除了不必要的符號並產生最佳的編碼長度。 過程中，每個終端節點都包含著一個權重（Weight、Probabilities、Frequency），兩兩終端節點結合會產生一個新節點，新節點的權重是由兩個權重最小的終端節點權重之總和，並持續進行此過程直到只剩下一個節點為止。 實現霍夫曼樹的方式有很多種，可以使用優先佇列（Priority Queue）簡單達成這個過程，給與權重較低的符號較高的優先順序（Priority），演演算法如下： ⒈把 n 個終端節點加入優先佇列，則 n 個節點都有一個優先權 Pi，1 i n ⒉如果佇列內的節點數 1，則： ⑴從佇列中移除兩個最小的 Pi 節點，即連續做兩次 remove（min（Pi） Priority_Queue ⑵產生一個新節點，此節點為（1）之移除節點之父節點，而此節點的權重值為（1）兩節點之權重和 ⑶把（2）產生之節點加入優先佇列中 ⒊最後在優先佇列裡的點為樹的根節點（root） 而此演演算法的時間複雜度（Time Complexity）為 O n log n；因為有 n 個終端節點，所以樹總共有 2n1 個節點，使用優先佇列每個迴圈須 O log n。 此外，有一個更快的方式使時間複雜度降至線性時間（Linear Time）O n，就是使用兩個佇列（Queue）創件霍夫曼樹。第一個佇列用來儲存 n 個符號（即 n 個終端節點）的權重，第二個佇列用來儲存兩兩權重的合（即非終端節點）。此法可保證最小值永遠都是兩個佇列的前端（Front）權重之一，且方法如下： ⒈把 n 個終端節點加入第一個佇列（依照權重大小排列，最小在前端） ⒉如果佇列內的節點數 1，則： ⑴從佇列前端移除兩個最低權重的節點 ⑵將（1）中移除的兩個節點權重相加合成一個新節點 ⑶加入第二個佇列 ⒊最後在第一個佇列的節點為根節點 雖然使用此方法比使用優先佇列的時間複雜度還低，但是注意此法的第 1 項，節點必須依照權重大小加入佇列中，如果節點加入順序不按大小，則需要經過排序，則至少花了 O n log n 的時間複雜度計算。 但是在不同的狀況考量下，時間複雜度並非是最重要的，如果我們今天考慮英文字母的出現頻率，變數 n 就是英文字母的 26 個字母，則使用哪一種演演算法時間複雜度都不會影響很大，因為 n 不是一筆龐大的數字。 資料解壓縮 簡單來說，霍夫曼碼樹的解壓縮就是將得到的前置碼（Prefix Huffman code）轉換回符號，通常藉由樹的追蹤（Traversal），將接收到的位元串（Bits stream）一步一步還原。但是要追蹤樹之前，必須要先重建霍夫曼樹；某些情況下，如果每個符號的權重可以被事先預測，那麼霍夫曼樹就可以預先重建，並且儲存並重複使用，否則，傳送端必須預先傳送霍夫曼樹的相關資訊給接收端。 最簡單的方式，就是預先統計各符號的權重並加入至壓縮之位元串，但是此法的運算量花費相當大，並不適合實際的應用。若是使用 Canonical encoding，則可精準得知樹重建的資料量只佔 B2B 位元（其中 B 為每個符號的位元數（bits））。如果簡單將接收到的位元串一個位元一個位元的重建，例如：0 表示父節點，1 表示終端節點，若每次讀取到 1 時，下 8 個位元則會被解讀是終端節點（假設資料為 8bit 字母），則霍夫曼樹則可被重建，以此方法，資料量的大小可能為 2320 位元組不等。雖然還有很多方法可以重建霍夫曼樹，但因為壓縮的資料串包含 trailing bits，所以還原時一定要考慮何時停止，不要還原到錯誤的值，如在資料壓縮時時加上每筆資料的長度等。 基本性質 考量到不同的應用領域以及該領域的平均性質，將會使用不同的通用機率，又或者，這個機率是可以從被壓縮文字中的實際頻率所取得。 最佳化 原始的霍夫曼演演算法是一個符號與符號間已知輸入機率分佈的最佳編碼方式，也就是說將不相關的符號個別編碼為如此的資料串流。然而，當符號間的限制被捨棄或是質量機率函式是未知的時候，如此方式則並非最佳化。此外，當這些符號之間不是互相獨立，且分佈不相同的時候，單一一個符號可能不足以實現最佳化。在這種情況之下，算術編碼可能比起霍夫曼編碼會有更佳的壓縮能力。 雖然上述兩種方法都可以組合任意數量的符號以實現更高效的編碼效果，並且通常適應於實際的輸入統計層面，但儘管最簡單的版本比霍夫曼編碼更慢且更複雜，算術編碼不會顯著增加其計算或演算法複雜度。當輸入機率不是精確已知或在流內顯著變化時，這種靈活性特別有用。然而，霍夫曼編碼通常更快，並且算術編碼在歷史上是專利問題的一些主題。因此，許多技術歷來避免使用有利於霍夫曼和其他字首編碼技術的算術編碼。截至 2010 年中期，隨著早期專利的到期，這種替代霍夫曼編碼的最常用技術已經進入公有領域。 對於具有均勻機率分佈的一組符號，以及作為 2 的冪之成員，霍夫曼編碼等同於簡單的二進位制編碼，例如 ASCII 編碼。這反映瞭如此的事實：無論壓縮方法是什麼，這種輸入都不可能進行壓縮，或只是說對資料無所作為，比起壓縮才是最佳選擇。 在任何情況下，霍夫曼編碼在所有方法中是最佳的方式，其中每個輸入符號是具有二元機率的已知獨立且相同分佈的隨機變數。字首碼，特別是霍夫曼編碼，往往在小字母表上產生較差的效率，其中機率通常落在這些最佳（二元）點之間。當最可能符號的機率遠超過 05 時，可能發生霍夫曼編碼的最壞情況，使低效率的上限無限制。 在使用霍夫曼編碼的同時，有兩種相關的方法可以解決這種特定的低效問題。將固定數量的符號組合在一起（阻塞）通常會增加（並且永不減少）壓縮。隨著塊的大小接近無窮大，霍夫曼編碼理論上接近熵限制，即最佳壓縮。然而，阻塞任意大的符號組是不切實際的，因為霍夫曼程式碼的複雜性在要編碼的可能性的數量上是線性的，這是在塊的大小中呈指數的數字。這限制了在實踐中完成的阻塞量。 廣泛使用的實際替代方案是行程編碼。該技術在熵編碼之前增加一步，特別是對重複符號進行執行次數的計數，然後對其進行編碼。對於伯努力（Bernoulli）過程的簡單情況，哥倫（Golomb）編碼在編碼遊程長度的字首碼中是最佳的，這是透過霍夫曼編碼技術證明的事實。使用改進的霍夫曼編碼的傳真機採用類似的方法。但是，遊程編碼並不像其他壓縮技術那樣適應許多輸入型別。 變化 霍夫曼編碼有衍生出許多的許多變化，其中一些使用類似霍夫曼的演算法，而其他一些演算法找到最佳字首碼（例如，對輸出施加不同的限制）。注意，在後一種情況下，該方法不需要像霍夫曼那樣，實際上甚至不需要到多項式複雜度。 多元樹霍夫曼編碼 多元樹霍夫曼編碼演演算法使用字母集 0 1 n 1 ，來構建一棵 n 元樹。霍夫曼在他的原始論文中考慮了這種方法。這與二進位制（n2）演算法僅有的差別，就是它每次把 n 個最低權的符號合併，而不僅是兩個最低權的符號。倘若 n 2 則並非所有源字集都可以正確地形成用於霍夫曼編碼的多元樹。在這些情況下，必須新增額外的零機率佔位符。這是因為該樹必須為滿的 n 叉樹，所以每次合併會令符號數恰好減少 n 1 故這樣的 n 叉樹存在當且僅當源字的數量模 n 1 餘 1 對於二進位制編碼，任何大小的集合都可以形成這樣的二叉樹，因為 n 1 1 自適應霍夫曼編碼 自適應霍夫曼編碼的變化，涉及基於源符號序列中的最近實際頻率動態地計算機率，以及改變編碼樹結構以匹配更新的機率估計。它在實踐中很少使用，因為更新樹的成本使其比最佳化的自適應算術編碼慢，後者更靈活並且具有更好的壓縮。 霍夫曼模板演演算法 在霍夫曼編碼的實現中，通常會使用權重表示數值機率，但是上面給出的演算法不需要這樣；它只需要權重形成一個完全有序的可交換麼半群，這意味著一種訂購權重和新增權重的方法。霍夫曼模板演算法使人們能夠使用任何型別的權重（成本，頻率，權重對，非數字權重）和許多組合方法之一（不僅僅是加法），這個問題首先應用於電路設計。 長度限制霍夫曼編碼 最小變異霍夫曼編碼 長度限制霍夫曼編碼 長度受限的霍夫曼編碼是一種變體，其目標仍然是實現最小加權路徑長度，但是存在另外的限制，即每個碼字的長度必須小於給定常數。包合併演算法透過一種與霍夫曼演演算法非常相似的簡單貪婪方法解決了這個問題。 不相等成本霍夫曼編碼 在標準的霍夫曼編碼問題中，假設集合中構成碼字的每個符號具有相同的傳輸成本：長度為 N 位的碼字總是具有 N 的成本，無論多少這些數字是 0，有多少是 1，等等。在這個假設下工作時，最小化訊息的總成本和最小化數字總數是相同的。 具有不等字母成本的霍夫曼編碼是沒有這種假設的概括：由於傳輸介質的特性，編碼字母表的字母可能具有不均勻的長度。一個例子是摩爾斯電碼的編碼字母表，其中 破折號 比 點 需要更長的傳送時間，因此傳輸時間中破折號的成本更高。目標仍然是最小化加權平均碼字長度，但僅僅最小化訊息使用的符號數量已經不夠了。沒有演算法以與傳統霍夫曼編碼相同的方式或相同的效率來解決這個問題，儘管它已經由卡普（Karp）解決，其解決方案已經針對哥林（Golin）的整數成本的情況進行了改進。 最佳字母二元樹 在標準霍夫曼編碼問題中，假設任何碼字可以對應於任何輸入符號。在字母表中，輸入和輸出的字母順序必須相同。 規範霍夫曼編碼 如果對應於按字母順序排列的輸入的權重是按數字順序排列的，則霍夫曼程式碼具有與最佳字母程式碼相同的長度，這可以從計算這些長度中找到，從而不需要使用胡 塔克（HuTucker）編碼。由數字（重新）有序輸入產生的程式碼有時被稱為規範霍夫曼程式碼，並且由於易於編碼 解碼，通常是實踐中使用的程式碼。用於找到該程式碼的技術有時被稱為霍夫曼 夏農 法諾編碼，因為它像霍夫曼編碼一樣是最優的，但是在重量機率上是字母的，例如夏農 法諾編碼。 資料長度 設符號集合 S lefts_1s_2cdotss_nright，Pleft s_jright 為 s_j 發生的機率 ，Lleft s_jright 為 s_j 編碼的長度 熵（Entropy） 亂度 entropy sum_j1JP left s_jrighttimesln P left s_jright ex P S_01entropy 0 P S_0P S_105entropy 06931 P S_0P S_1P S_2P S_3P S_402entropy 16094 P S_0P S_1P S_2P S_301P S_406entropy 12275 第三與第四個範例，同樣是五種組合，機率分佈越集中，則亂度越少 霍夫曼碼平均長度 mean left L right sum_j1JP left s_jrighttimes L left s_jright 霍夫曼碼的長度 Shannon 編碼定理： entropy over logk mean left L right entropy over logk 1 ，若使用 k 進位的編碼 霍夫曼碼的平均編碼長度：設 b mean left L right N ，N 為資料長度 N entropy over logk b N entropy over logk N 範例 當有 100 個位子，有白色佔 60、咖啡色佔 20，藍色和紅色各佔 10，則該如何分配才能最佳化此座位？a direct 假設結果為：白色為 00、咖啡色 01，藍色 10 和紅色 11 個 bits 則結果為：1002 200bits b huffman code must be satisfy the following conditions，if not change the node 1 所有碼皆在 Coding Tree 的端點，再下去沒有分枝 滿足一致解碼跟瞬間解碼 2 機率越大，code length 越短；機率越小，code length 越長 3 假設 S_a 是第 L 層的 node，S_b 是第 L1 層的 node 則 P S_a geq P S_b 必須滿足 假設結果為：白色佔 0、咖啡色 10，藍色 110 和紅色 111 個 bits 則結果為：601202203160bits 相互比較兩個結果 huffman code 整整少了 40bits 的儲存空間。 示範程式 以下為 C 程式碼，在 G 下編譯透過 僅用於示範如何根據權值建構霍夫曼樹， 沒有經過效能上的最佳化及加上完善的異常處理。 include cstdlib include iostream include deque include algorithm using namespace std const int size 10 struct node 霍夫曼樹節點結構 unsigned key 儲存權值 node lchild 左孩子指標 node rchild 右孩子指標 dequenode forest dequebool code 此處也可使用 bitset node ptr int frequency size 0 void printCode dequebool ptr 用於輸出霍夫曼編碼 bool compare node a node b return akey bkey int main int argc char argv for int i 0 i size i cin frequency i 輸入 10 個權值 ptr new node ptrkey frequency i ptrlchild NULL ptrrchild NULL forestpush_back ptr 形成森林，森林中的每一棵樹都是一個節點 從森林構建霍夫曼樹 for int i 0 i size 1 i sort forestbegin forestend compare ptr new node 以下程式碼使用下標索引佇列元素，具有潛在危險，使用時請注意 ptrkey forest 0key forest 1key ptrlchild forest 0 ptrrchild forest 1 forestpop_front forestpop_front forestpush_back ptr ptr forestfront ptr 是一個指向根的指標 system PAUSE return EXIT_SUCCESS void printCode dequebool ptr dequebool for int i 0 i ptrsize i if ptr i cout 1 else cout 0 霍夫曼編碼 C 程式碼 huffmanh 檔案 ifndef __HUFFMAN_H__ define __HUFFMAN_H__ includestdioh includestdlibh includestringh define START printf startn define END printf endn define toByte n n 8 n 8 0 typedef struct HuffListNode HuffListNodehufflistnodep typedef struct HuffNode HuffNodehuffnodep typedef struct HuffTree HuffTreehufftreep typedef struct HuffCode HuffCodehuffcodep typedef struct HuffList HuffListhufflistp typedef struct HuffResult HuffResulthuffresultp typedef struct HuffCode HuffBufhuffbufp 快取型別 struct HuffListNode huffnodep node huffman 節點 hufflistnodep next 後繼節點 huffman 頻率節點 struct HuffList hufflistnodep head 頭結點 int keys 256 鍵值字典 int size 連結串列長度 struct HuffNode int key 鍵 int weight 權重 huffnodep left 左節點 huffnodep right 右節點 huffman 節點 struct HuffCode char code huffman code int size huffman code size struct HuffTree huffnodep root 根 huffcodep codes 256 key 對應的程式碼 int size 大小 huffman 樹 struct HuffResult char code 生成的程式碼 hufftreep tree 對應的霍夫曼樹 ifndef __BOOLEAN__ define __BOOLEAN__ typedef enum FALSE 0 TRUE 1 Boolean endif huffnodep huffnode int keyint weight 初始化 huffman 節點 hufflistp hufflist 初始化 hufflist Boolean insertHuffNode hufflistp listhuffnodep node 向指定的節點連結串列新增一個節點 huffnodep shiftHuffNode hufflistp list 刪除第一個節點 hufftreep hufftree hufflistp list 構建一棵 huffman tree huffbufp getFileBuf const char filename 獲取檔案的 buf hufftreep genhuffcodes hufftreep treehuffnodep nodechar codes int idx 獲取當前節點之下的節點的 huffman 編碼 hufflistp getHuffListByFile const char filename 根據檔案建立 huffman 連結串列 hufflistp getHuffListByBuf huffbufp buf 根據檔案 buf 建立 huffman 連結串列 huffcodep getHuffCode hufftreep treeint key 獲取指定鍵值的 huffcode huffresultp getHuffCodesByFile const char filename 獲取檔案的 huffman code huffresultp getHuffCodesByBuf huffbufp buf 透過 buf 獲取 codes huffbufp getOriginBuf huffresultp result 從 result 中解析出原始的字串 huffbufp str2bin char str 二進位制字串轉二進位制陣列 int putOriginToFile huffresultp resultconst char filename 將 result 儲存到 filename 中 char bin2str huffbufp buf 二進位制陣列轉二進位制字串 huffbufp readHuffFile const char filename 解析 huff 檔案 endif huffmanc 檔案 include huffmanh huffnodep huffnode int keyint weighthuffnodep ret huffnodep malloc sizeof HuffNode retkey key retweight weight retleft retright NULL return ret hufflistp hufflist hufflistp ret hufflistp malloc sizeof HuffList rethead NULL memset retkeys0sizeof retkeys 0256 retsize 0 return ret Boolean insertHuffNode hufflistp listhuffnodep nodeif list NULL node NULL nodeweight 256 return FALSE hufflistnodep cur listhead hufflistnodep rootp listhead hufflistnodep last NULL 當前指標的前驅指標 hufflistnodep tmp hufflistnodep malloc sizeof HuffListNode tmpnode node tmpnext NULL if nodekey 0 nodekey 256listkeys nodekey nodeweight 新增 key 到 keys 字典 listsize for cur NULL curnodeweight nodeweight cur curnext last rootp rootp curnext tmpnext cur if last NULL 第一個元素 listhead tmp else 向當前節點前面插入 tmp 節點 lastnext tmp return TRUE huffnodep shiftHuffNode hufflistp listif list NULL listhead NULL return NULL huffnodep ret listheadnode hufflistnodep next listheadnext free listhead listhead next listsize return ret 透過 huffman list 構建 hufftreep hufftree hufflistp listhufftreep tree hufftreep malloc sizeof HuffTree treeroot NULL treesize 0 memset treecodes0sizeof treecodes huffnodep a NULL huffnodep b NULL huffnodep c NULL treesize 2 listsize 1 while listsize 1 hufflistp 長度大於 1 a shiftHuffNode list b shiftHuffNode list c huffnode 256aweightbweight 新的節點 cleft a cright b insertHuffNode listc 將 c 壓回 list treeroot c 生成所有 key 的 huffman 編碼 char codes 8092 huffman 編輯路徑 return genhuffcodes treetreerootcodes0 獲取檔案內容的 BUF huffbufp getFileBuf const char filenameFILE fp fopen filenamer if fp NULL return NULL fseek fp0LSEEK_END int len ftell fp rewind fp 重設 huffbufp ret huffbufp malloc sizeof HuffBuf retcode char malloc len1 retsize len fread retcode1lenfp fclose fp return ret hufftreep genhuffcodes hufftreep treehuffnodep nodechar codes int idxif tree NULL node NULL 到達底部 return NULL if nodeleft NULL noderight NULL 葉子節點 int key nodekey huffcodep code huffcodep malloc sizeof HuffCode codecode char malloc idx1 codesize idx memcpy codecodecodescodesize codecode codesize 0 treecodes key code codes idx 1 右 genhuffcodes treenoderightcodesidx1 codes idx 0 左 genhuffcodes treenodeleftcodesidx1 return tree 透過檔案生成 huffman list hufflistp getHuffListByFile const char filenamehuffbufp buf getFileBuf filename if buf NULL return NULL hufflistp list getHuffListByBuf buf free bufcode bufcode NULL free buf buf NULL return list hufflistp getHuffListByBuf huffbufp bufif buf NULL bufcode NULL return NULL char code bufcode hufflistp list hufflist for int i 0 code i 0 iunsigned char ch code i listkeys ch for int i 0 i 256 iif listkeys i 0 插入存在的字元 insertHuffNode listhuffnode ilistkeys i return list huffcodep getHuffCode hufftreep treeint keyif key 256 key 0 treecodes key 0return treecodes key return NULL huffresultp getHuffCodesByFile const char filenamehuffbufp buf getFileBuf filename 檔案快取 return getHuffCodesByBuf buf huffresultp getHuffCodesByBuf huffbufp bufhuffresultp result huffresultp malloc sizeof HuffResult resultcode NULL if buf NULL return NULL hufflistp list getHuffListByBuf buf huffman list resulttree hufftree list int buf_len bufsize int len 0 for int i 0 bufcode i 0 iint key unsigned char bufcode i huffcodep code getHuffCode resulttreekey if code NULLprintf LLL c dnkeykey return NULL lencodesize resultcode char malloc len1 resultcode 0 0 for int i 0 bufcode i 0 iunsigned char key bufcode i huffcodep code getHuffCode resulttreekey strncat resultcodecodecodecodesize return result huffbufp getOriginBuf huffresultp resultif result NULL resultcode NULL resulttree NULL return NULL hufftreep tree resulttree char code resultcode int len 0 for int i 0 code i 0huffnodep root treeroot 根節點 while rootleft NULL rootright NULL code i 0 雙子節點存在 root code i 0 rootleft rootright i if rootleft NULL rootright NULL code i 0 錯誤 return NULL len printf 解析 c snrootkeytreecodes rootkeycode huffbufp ret huffbufp malloc sizeof HuffBuf retcode char malloc len1 retcode 0 0 retsize len int idx 0 for int i 0 code i 0huffnodep root treeroot 根節點 while rootleft NULL rootright NULL code i 0 雙子節點存在 root code i 0 rootleft rootright i retcode idx rootkey retcode idx 0 return ret int putOriginToFile huffresultp resultconst char filenameif result NULL return 0 printf res1 d snint strlen resultcoderesultcode huffbufp b str2bin resultcode printf dnbsize printf res2 snbin2str b return 0 huffbufp buf str2bin resultcode huffman code 轉成 buf int i 0 int len 0 for i 0 i 256 iif resulttreecodes i 0 len 5resulttreecodes isize key 1len 4size huffbufp keys huffbufp malloc sizeof HuffBuf keyscode char malloc len keyssize 0 獲取 keys int idx 0 for i 0 i 256 iif resulttreecodes i 0 keyscode idx i key int len resulttreecodes isize memcpy keyscodeidxlen4 key size printf c d d sniilenresulttreecodes icode idx4 huffbufp tmp str2bin resulttreecodes icode printf d dntmpcode 0tmpsize int tsize toByte tmpsize memcpy keyscodeidxtmpcodetsize idxtsize keyssize idx 諸多鍵的總空間 寫出標準檔案 HUFn size 4b keys size 4b codes FILE fp fopen filenamew if fp NULL return 1 fwrite HUFn14fp fwrite idx14fp size fwrite keyscode1keyssizefp 寫入 code fwrite bufsize14fp size fwrite bufcode1toByte bufsizefp fclose fp return 44keyssize4bufsize huffbufp str2bin char str 二進位制字串轉二進位制陣列 printf bin snstr if str NULL return NULL huffbufp buf huffbufp malloc sizeof HuffBuf int l strlen str int size l 8 l 8 0 bufcode char malloc l memset bufcode0l for int i 0 i l i int idx i8 int bi i8 bufcode idx str i 0 01 bi bufsize l return buf char bin2str huffbufp bufchar ret char malloc bufsize1 for int i 0 i bufsize i int idx i 8 int offset i 8 ret i bufcode idx 0x01 offset 1 0 ret bufsize 0 return ret huffbufp readHuffFile const char filenamehuffbufp buf getFileBuf filename if buf NULL return NULL if memcmp bufcodeHUFn4 0 return NULL 檔案不以 BUFn 開頭 huffresultp result huffresultp malloc sizeof HuffResult BUFn key size int key_size intbufcode4 int base 8 偏移量 hufftreep tree hufftreep malloc sizeof HuffTree treeroot NULL treesize 0 huffcodep codes treecodes key 對應程式碼 memset codes0sizeof huffcodep256 int oft 0 for oft key_size int offset baseoft unsigned char key bufcode offset printf d cnkeykey int size intbufcodeoffset1 長度 int byte toByte size huffbufp htmp huffbufp malloc sizeof HuffBuf 鍵對應程式碼 htmpcode bufcodeoffset5 快取程式碼 htmpsize size 快取大小 printf c dnkeykey huffcodep tmp huffcodep malloc sizeof HuffCode tmpsize size key 的大小 tmpcode bin2str htmp treecodes key tmp treesize 樹的大小增加 huffnodep root treeroot if root NULLtreeroot huffnode 2560 root treeroot for int i 0 i tmpsize ichar ch tmpcode i huffnodep node NULL if ch 0node rootleft if node NULLnode huffnode 2560 rootleft node else node rootright if node NULLnode huffnode 2560 rootright node if i tmpsize 1 nodekey key root node oft5byte huffbufp tmp huffbufp malloc sizeof HuffBuf tmpcode bufcodebaseoft4 tmpsize intbufcodebaseoft printf tmp size dntmpsize resulttree tree resultcode bin2str tmp printf snresultcode for int i 0 i 256 i if codes iNULL printf c d sniicodes icode return getOriginBuf result 程式演示主檔案 includehuffmanh int main huffbufp buf huffbufp malloc sizeof HuffBuf bufcode this is just a test bufsize strlen bufcode huffresultp result getHuffCodesByBuf buf 獲取編碼結果 printf huffman snresultcode huffbufp origin getOriginBuf result 透過編碼獲取原始資料 printf origin snorigincode return 0 參考文獻 Thomas H Cormen Charles E Leiserson Ronald L Rivest and Clifford Stein Introduction to Algorithms Second Edition MIT Press and McGrawHill 2001 Section 163 pp 385392 外部連結 有關霍夫曼壓縮技術的原文：DA Huffman A method for the construction of minimumredundancy codes Proceedings of the IRE sept 1952 pp 10981102 霍夫曼樹圖形演示 Animation of the Huffman Algorithm Algorithm Simulation by Simon Mescal Background story Profile David A Huffman Scientific American Sept 1991 pp 5458 nary Huffman Template Algorithm Huffman codes connection with Fibonacci and Lucas numbers Fibonacci connection between Huffman codes and Wythoff array Sloane A098950 Minimizing kordered sequences of maximum height Huffman tree Computing Huffman codes on a Turing Machine Mordecai J Golin Claire Kenyon Neal E Young Huffman coding with unequal letter costs STOC 2002 785791 Huffman Coding implemented in python H Category 無失真壓縮演算法 Category 編碼理論 Category 資料壓縮