【什么是編碼】
例:給出一段字符串,它只包含A、B、C、D、E這5種字符。字符出現頻率不同,如下表。現對其進行二進制編碼,要求無二義性且碼文盡可能短。
1.等長編碼
最簡單的編碼方法是把每個字符都用於都用相同長度的二進制數來表示,如下表。
顯然無二義性,每個字符用3位二進制數表示,存儲的總長度是:3 * (3+9+6+15+19) = 156。
2.變長編碼
出現次數多的字符用短碼表示,出現少的用長碼表示,如下表。
可以看到,此時也沒有二義性,例如"1100 111 10 0 1101",解碼后唯一得到"ABDEC"。
存儲的總長度是:3 * 4 + 9 * 3 + 6 * 4 + 15 * 2 + 19 * 1 = 112。第二種方法相當於對第一種方法,壓縮比是:156/112=1.39。
但是,變長編碼有其缺陷:胡亂設定編碼方案,很可能錯誤,例如:
存在二義性,編碼無法解碼還原。例如"100",是"A"、"BE"還是"DEE"呢?
錯誤的原因是,某個編碼是另一個編碼的前綴(prefix),即這兩個編碼有包含關系,導致了混淆。
因此需要尋找一套編碼方式, 使得其中任何一個字符的編碼都不是另一個字符的編碼的前綴,同時把滿足這種編碼方式的編碼稱為前綴編碼。Huffman編碼便是前綴編碼算法中的最優算法,是能使給定字符串編碼成01串后長度最短的前綴編碼。
【哈夫曼樹與哈夫曼編碼】
1.概念
把葉子結點的權值乘以其路徑長度的結果稱為這個葉子結點的帶權路徑長度,其中葉子結點的路徑長度是指從根結點出發到達該結點所經過的邊數。(例如上面的例子中,權值為 66 的葉子結點的帶權路徑長度為 6∗2=126∗2=12,而權值為 11 的葉子結點的帶權路徑長度為 1∗3=31∗3=3
樹的帶權路徑長度 (Weighted Path Length of Tree, WPL) 等於它所有葉子結點的帶權路徑長度之和。
對同一組葉子結點來說,哈夫曼樹可以是不唯一的,但是最小帶權路徑長度一定是唯一的。
Huffman編碼是利用貪心思想構造二叉編碼樹的算法。如果把A、B、C、D、E的出現次數 (即頻數) 作為各自葉子結點的權值,那么字符串編碼成01串后的長度實際上就是這棵樹的帶權路徑長度。
2.構造
對所有字符(即葉子結點)按頻次排序:
在這里插入圖片描述
從最少的字符開始,用貪心思想安排在二叉樹上。現把A、C放到二叉樹上:
在這里插入圖片描述
把B放到二叉樹上,調整D:
在這里插入圖片描述
把D放到二叉樹上,調整E:
把E放到二叉樹上,得到最終的哈夫曼樹。
將哈夫曼樹的左樹枝記為0,右樹枝記為1,便得到哈夫曼編碼,如下表:
通過這個例子也可以發現,對哈夫曼樹來說不存在度為1的結點,並且權值越高的結點相對來說越接近根結點。