哈夫曼樹以及檔案壓縮的實現
一、HuffmanTree
哈夫曼樹也稱為最優二叉樹,是加權路徑長度最短的二叉樹。在講述哈夫曼樹之前先給出幾個概念:
路徑:從一個結點到一個結點之間的分支構成這兩個結點之間的路徑
路徑長度:路徑上分支的數目稱為路徑長度
樹的路徑長度:從根節點到每一個結點的路徑長度之和
結點的帶權路徑長度:從該節點到樹根之間的路徑長度與節點上權值的乘積
樹的帶權路徑長度(WPL):樹中所有葉子節點的帶權路徑長度之和
分析上圖中的兩棵樹:
(a)樹的路徑長度 = 1+1+2+2+3+3+4+4 = 20
WPL = 5*1 + 15*2 + 40*3 + 30*4 + 10*4 = 315
(b)樹的路徑長度 = 1+1+2+2+2+2+3+3 = 16
WPL = 5*3 + 15*3 + 40*2 + 30*2 + 10*2 = 220
通過比較我們發現,二叉樹a的路徑長度和WPL值都大於二叉樹b,而且上邊也給出了哈夫曼樹概念,因此在這裡二叉樹b其實就是一棵哈夫曼樹。那麼我們如何才能構造這樣一棵樹呢?!
二、哈夫曼樹的構造
其實哈夫曼最早給出了構建樹的演算法,稱為哈夫曼演算法,這個演算法大家在網上搜搜也就出來了,因此我在這裡就不再寫了。但是,我相信很多人看了這個都會很蒙圈,所以我就利用圖+文字的形式來構建一棵哈夫曼樹。
我們在構建這棵哈夫曼樹時可利用"貪心演算法"的思想來構建,也就是說,每次只考慮區域性最優解
(1)先將所有的權值按照從小到大的順序進行排序,例如為n1,n2,n3,n4,n5,......
(2)每次選出頭兩個最小權值的結點n1,n2來構建n1和n2的父親節點N1,其做法是:n1與n2的權值相加作為N1的權值,並且n1和n2中權值較小的作為左孩子
(3)用得到的N1替換n1與n2,可得到N1,n3,n4,n5,.....,每次都要保持從小到大的順序
(4)重複步驟(2)(3),當所有結點都在樹中時,這棵樹也就構建好了
需要說的一點是,當在進行步驟(2)(3)時,若遇到兩個數的和正好是下一步的兩個最小數的其中一個時,則這棵樹就繼續向上生長;若是兩個數的和比較大不是下一步的兩個最小數中的一個,就並列生長。
例如:我們利用{0,1,2,3,4,5,6,7,8,9}這組數來構建哈夫曼樹
三、哈夫曼編碼
哈夫曼研究這種最優二叉樹的更大目的是為了解決當年遠距離通訊的資料傳輸的最優化問題,其實哈夫曼樹最經典的一種應用就是哈夫曼編碼,現在我們可以利用哈夫曼編碼來進行檔案壓縮。
哈夫曼編碼:假若我們規定給每個結點的左路標記'0',右路標記'1',那麼我們就可以用0、1來表示每個字母。以第一幅圖中的二叉樹b為例
四、檔案壓縮
檔案壓縮的主要思想是利用哈夫曼編碼來實現的,但是得到編碼之前我們需要構建這棵樹。那麼利用什麼來構建樹呢?!這裡,我們需要統計每個字元出現的次數,用次數來構建HuffmanTree。假設我們現在有一個.txt的小檔案,內容是"aaaabbbccd"。字元存在計算機中時以位元組為單位的,因此我們需要將這些字元壓縮成0、1表示的編碼,0和1表示位元組中的“位”,這樣能大大降低檔案的大小。
上圖中的文字就是檔案壓縮的4個步驟。
對第4步的結果進行對比可以看出,原始檔需要的大小是10個位元組,壓縮後的檔案是使用了19個位元位的空間,其實也就是3個位元組的大小,剩下不夠的5個位元位用0來補。這樣其實也就時檔案壓縮後的效果!
五、檔案解壓
檔案解壓時就需要從根節點向葉子節點走了,讀取壓縮檔案的編碼,遇到'0'就向樹的左邊走,遇到'1'就向樹的右邊走。由於這些字元肯定都是存在與葉子結點上的,因此當遇到葉子節點以後就說明已經還原出一個字元,這時將還原的一個字元寫入解壓檔案中;解壓完一個字元後再次開始從根節點向下遍歷。
解壓檔案時會遇到的問題:
上邊說過,壓縮以後的檔案如果不夠一個位元組大小,會用0來代替空缺的位元位,這樣帶來的問題就是可能會多解壓出字元。以上述的"aaaabbbccd"為例,壓縮以後的編碼為19位元位:00001111 11110110 100,計算機儲存的最小單位為位元組,因此這些編碼會被儲存為00001111 11110110 10000000,後邊的五個0對我們來說就是多餘的,如果不處理,就會多還原出5個a
解決:
給出一個charCount來統計所有字串的個數,其實也就是根節點的值,每次還原出一個字元後,charCount就-1,直到charCount為0時說明所有字元都已經解壓完成。
PS:以上所有程式碼我都上傳至github,可以點這個連結檢視原始碼:
六、測試用例
我分別測試了.txt檔案、.mp3檔案、.jpg檔案,還有一個自己定義的.BIG的檔案
以Input.BIG檔案壓縮為例,其進行壓縮與解壓後的結果如下:
我們不能僅用解壓檔案與原始檔的大小來判斷是否解壓成功,可以藉助BeyondCompared軟體來比對,對比結果如下(頁面中沒有顯示紅色的字型就表示檔案一樣):