直接採用huffman壓縮思想的缺陷及改進

上一篇我們對自己實現的基於GZIP的壓縮算法進行測試,對於有些文件壓縮效率還是可觀的,不過,直接採用huffman壓縮思想卻存在一定缺陷。

1.缺陷:
①需要創建huffman樹,如果不同種類字節出現比較多的情況下,huffman樹會很大。
比如:待壓縮文件中有250個不同字符種類(一個字節能表示最多256個不同的字節),該250個字節都在huffman樹葉子節點的位置,那麼將來會有(250 + 249)個節點,佔用內存較大,且獲取編碼的效率低(遞歸獲取)

②huffman樹如果比較大,解壓縮效率低。由於解壓縮時不斷從根節點往葉子節點方向移動,重複次數多。

③爲了能夠解壓縮,壓縮完成後必須在壓縮文件中保存字符頻度信息。如果待壓縮中間字節不同種類多,出現次數比較均勻,那麼壓縮後字符頻度信息表將會佔很大空間,影響壓縮bu比率。

改進措施:不要直接採用huffman樹,推薦採用範式huffman樹
什麼是範式huffman樹:範式huffman樹是在huffman樹的基礎之上,進行了一些強制性的約定,即:對於同一層節點中,所有的葉子節點都調整到左邊,然後,對於同一層的葉子節點按照符號順序從小到大調整 ,最後按照左0右1的方式分配編碼 參考博客
在這裏插入圖片描述
此過程可大大降低編碼複雜度,根據特性即可計算出每個葉子節點的編碼。不過在計算葉子節點編碼之前,需要拿到每個葉子節點的碼字長度(該葉子節點在樹中的高度)

2.範式huffman樹的創建:
①模擬Huffman樹的創建(採用靜態數組來模擬huffman樹
遍歷壓縮文件,統計每個字符出現的頻度,然後採用數組的方式模擬huffman樹。
在這裏插入圖片描述
採用靜態數組構建huffman樹

範式huffman樹和普通huffman樹最終獲取的每個字節的編碼長度實際是一樣的
採用範式huffman樹的優點
①只需要用數組模擬出huffman樹,節省內存空間
②用範式huffman樹獲取編碼的效率更快,不需要再去遍歷huffman樹
③關於壓縮數據的保存,不需要保存頻度,只需保存編碼長度

將256個字符種類用前256個字節來保存編碼長度
比如:········000024340001232000········
每個字符對應位置保存編碼長度,沒有的就用0表示。

下面又引入新的問題?如何只通過編碼長度就能獲取字符編碼
解決方案:在上述方式的基礎上再進行了壓縮-----遊程編碼

3.什麼是遊程編碼?
例:4, 4, 4, 4, 4, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2
壓縮後:4, 16, 01(二進制), 3, 3, 3, 6, 16, 11(二進制), 16, 00(二進制),17,011(二進制), 2, 16, 00(二進制)
這是什麼意思呢?因爲CL的範圍是0-15,GZIP認爲重複出現2次太短就不用遊程編碼了,所以遊程長度從3開始。用16這個特殊的數表示重複出現3、4、5、6個這樣一個遊程,分別後面跟着00、01、10、11表示(實際存儲的時候需要低比特優先存儲,需要把比特倒序來存,博文的一些例子有時候會忽略這點,實際寫程序的時候一定要注意,否則會得到錯誤結果)。於是4,4,4,4,4,這段遊程記錄爲4,16,01,也就是說,4這個數,後面還會連續出現了4次。6,16,11,16,00表示6後面還連續跟着6個6,再跟着3個6;因爲連續的0出現的可能很多,所以用17、18這兩個特殊的數專門表示0遊程,17後面跟着3個比特分別記錄長度爲3-10(總共8種可能)的遊程;18後面跟着7個比特表示11-138(總共128種可能)的遊程。17,011(二進制)表示連續出現6
個0;18,0111110(二進制)表示連續出現62個0。總之記住,0-15是CL可能出現的值,16表示除了0以外的其它遊程;17、18表示0遊程。
因爲二進制實際上也是個整數所以上面的序列用整數表示爲
4, 16, 1, 3, 3, 3, 6, 16, 3, 16, 0, 17, 3, 2, 16, 0
參考文檔

4.基於Huffman編碼的壓縮和解壓縮
① 從壓縮數據中獲取符號的編碼位長,構建符號位長表。
②根據編碼位長建立解碼錶。(根據範式huffman樹的特點,前文講過)
③解碼

5.距離的壓縮
LZ77在查找緩衝區中找匹配時,最長的距離不會超過32K,即最大的距離爲32768,即距離的範圍是[1,32768],距離會非常多,雖然不會達到3276個,但是如果對於一個比較大的文件進行LZ編碼,distance上千還是很正常的,因此會導致huffman樹非常大,計算量和內存消耗都會超過當時的硬件條件,怎麼辦呢?
GZIP提供了一種非常好的方式,將distance劃分成多個區間,每個區間當做一個整數來看,該整數稱爲Distance Code。當一個distance落到某個區間,則相當於出現了那個Code,雖然distance很多,Distance Code可以劃分少一點,即多個distance對應一個Distance Code,最後只需要對Distance Code
進行huffman編碼即可。得到Code後,Distance Code再根據一定規則擴展出來。GZIP最終將distance劃分成了30個區間
在這裏插入圖片描述

6.原字符和長度的壓縮
原字符表示在LZ77中未匹配的字符,長度表示重複字符串的個數,都佔了一個字節,因此GZIP將其壓縮合二爲一了,即對於原字符和距離採用同一棵huffman樹進行處理。原字符的範圍是[0, 255],距離是[3, 258],如何進行處理呢?
GZIP用整數0~255表示原字符,256表示結束標誌,即解碼以後是256表示解碼結束,從257開始表示距離,比如:257表示重複3個字符,258重複4個字符,但GZIP並沒有一直這麼一一對應,而是採用了和distance
類似的方式進行分區,總共將距離劃分成了29個區間.
在這裏插入圖片描述

筆者能理解就這麼多,有興趣可以自己在網上找資料。

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