給你40分鐘的時間,你可以思考十分鐘,然後用三十分鐘的時間來寫**,最後浪費在無謂的除錯上;你也可以思考半個小時,徹底弄清問題的本質與程式的脈絡,然後用十分鐘的時間來編寫**,體會**如行雲流水而出的感覺。在程式設計過程當中,相信大家都深有體會,在除錯上浪費時間,問題出現在下筆之前沒有乙個系統結構。
哈夫曼在通訊領域有很多的用途,將需要傳輸的資料轉換01串,相比直接傳輸,極大提高了傳輸的速率,同時還在資料壓縮的重要方法。而本篇主要介紹的是哈夫曼壓縮演算法。
建立huffman樹的基本思路:
給定有權重的一系列資料(帶權重),從中挑選最小權重的兩個資料,組成一棵樹,得到的父節點再插入到資料系列當中。假設一字串是,「abcabcabcabcabcabcddddddddd」,統計各字元出現的次數如下表。
字元出現次數a6
b6c6
d9按照一搬的儲存方法,乙個字元占用乙個位元組,那麼共花費(6+6+6+9)*sizeof(char) = 27位元組,27位元組確實不算什麼,但是如果是海量資料的時候,就可能要考慮儲存空間的問題了。
來看看哈夫曼壓縮演算法是怎麼做的,同樣是上面的例子,我們試著建立哈夫曼樹,出現的次數就當做是權重,出現的次數越多的話,越靠近根節點,那麼編碼越短,如下圖:
於是上面的「abcabcabcabcabcabcddddddddd」,就可以轉化為「0001,1000,0110,0001,1000,0110,0001,1000,0110,1111,1111,1111,1111,11」,注意這裡採用的按位儲存的,也就是說0和1都是位,而非char型。那麼之前的27位元組就被我們轉換成了7個位元組(7位元組不足,不足的話就補零),而這就達到了壓縮的效果。
所以總結一下:利用哈夫曼樹編碼的特點,權重越大越靠近根節點,得到的編碼就越短的原理,而如果把字元出現次數作為權重的話,文字當**現次數最多的字元就被壓縮成了很短的編碼。
·壓縮過程主要步驟如下:
統計:讀入原始檔,統計字元出現的次數(即統計權重),順便根據權重進行從大到小的排序(主要的話之後的操作會簡單一些);
建樹:以字元的權重(權重為0的字元除外)為依據建立哈夫曼樹;
編碼:依據2中的哈夫曼樹,得到每乙個字元的編碼;
寫入:新建壓縮檔案,寫入壓縮資料。
其中最為複雜的是步驟4,因為它涉及到了位的操作,待我細細道來。
假設一字串是,「acbcbacddaddaddccd」,統計各字元出現的次數如下表。
字元出現次數a4
步驟123統計,建樹,編碼都已經完成了,剩下寫入壓縮檔案。將字串一步一步翻譯成01串,
1 0110
1011 0…
似乎都很順利,但是位操作有點麻煩。首先申請足夠大的記憶體,比如已知文字字元個數是1000個字元(位元組),可以申請1000*4,即乙個位元組平均4位元組(32位)的壓縮編碼空間(已經足夠大了),別把這些看成是char型了,當作位來看。
宣告足夠大的記憶體
可能說的不夠清楚,所以畫了圖:
*(long *)(pdest+(ncodeindex>>3)) |= (p->code) << (ncodeindex&7);(其中p為哈夫曼節點)
如此一來,我們就可以很理直氣壯的將*(long *)(pdest+(ncodeindex>>3))賦值為*(long *)(pdest+(ncodeindex>>3)) | (p->code) << (ncodeindex&7)(0|x=x),而不用擔心到底*(long *)(pdest+(ncodeindex>>3))裡面到底有多少位已經是儲存了的。
給出壓縮主要**:
void compresshuffman(char * filename)
//·寫入原始檔的字元個數
fshuff.write((char *)&nfilelen,sizeof(long));
//·寫入編碼長度
fshuff.write((char *)&ncodeindex,sizeof(int));
unsigned int ndestlen = ncodeindex/8;
if(ndestlen*8 < ncodeindex)
ndestlen ++;
//·寫入編碼
fshuff.write(pdest,ndestlen);
fshuff.close();
fs.close();
}
前一段時間在接觸點陣圖的時候被點陣圖結構觸動了,感覺它儲存得有條理,於是萌生了為哈夫曼壓縮檔案定義乙個儲存結構,稱之為哈夫曼壓縮檔案dl結構。關鍵是要統一,這篇博文用的是一種結構,另一篇用的又是另一種,紛雜的樣式會讓初學者發暈,所以統一結構對於學習哈夫曼壓縮檔案會有很大的幫助。dl結構組成部分:
為了便於讓你不看文字就能明白,看下圖,按著這種結構就相當於有了大概的思路。
解壓的過程就簡單很多了,因為一些**已經在解壓過程當中完成,比如哈夫曼樹的建立,我們只要設計壓縮和解壓通用的介面,就可以很簡單的按照編碼域的內容,將編碼翻譯成原文。
讀入節點個數;
根據1,讀入節點域;
建立哈夫曼樹;
讀入編碼長度;
根據4,讀入編碼域;
翻譯;寫入解壓後的檔案。
根據編碼長度(以bit為單位),可以計算出編碼域的大小(以byte為單位),讀入編碼域就很方便了。其中翻譯部分我給出一部分**,根據哈夫曼樹,將編碼域的01串按位處理,轉換為字元。
for(i=0; i=0; j--)
code>>=1; //·為了處理下一位,右移一位
} nsrcindex ++;
} code = *(temp+(nsrcindex));
for(int j=0; jright : p->left;
if(!p->left && !p->right)
code>>=1; //·為了處理下一位,右移一位
}
附哈夫曼壓縮演算法工程:
哈夫曼壓縮演算法.rar
本文完。monday, december 26, 2011
哈夫曼壓縮
哈夫曼演算法 huffman 演算法是一種基於統計的壓縮方法。它的本質就是對文字檔案中的字元進行重新編碼,對於使用頻率越高的字元,其編碼也越短。但是任何2 個字元的編碼,是不能出現向前包含的。也就是說字元a 假設為00 的編碼的前段,不可能為字元b 則b的編碼不可能為001 因為這裡b 的編碼中包含...
哈夫曼壓縮
哈夫曼壓縮 哈夫曼壓縮是無失真壓縮的一種,一般用來壓縮文字和程式檔案。壓縮步驟 將要壓縮的檔案乙個乙個位元組地讀出來,統計每個位元組出現的次數 作為該葉子節點的權值 構建哈夫曼樹,採用優先佇列。遍歷哈夫曼樹,得到每乙個葉子節點的哈夫曼編碼。左0右1,得到的是01字串 將不足八位的01字串補0使其達到...
哈夫曼壓縮
size large 此文主要分析的是哈夫曼壓縮的重點包括統計字元頻率,建哈夫曼樹,生成碼表。哈夫曼壓縮是最常用的一種靜態無痕壓縮。以前也學習過哈夫曼的演算法結構,但是沒有自己去寫 實現,這次再學習了一遍,更加深刻理解哈夫曼壓縮的原理,如何真正實現檔案的壓縮節省記憶體資源。下面梳理下我的 和分析邏輯...