1. 程式人生 > >基於哈夫曼編碼的文字檔案壓縮與解壓縮

基於哈夫曼編碼的文字檔案壓縮與解壓縮

基於哈夫曼編碼實現檔案壓縮

是在學習資料結構(嚴蔚敏版)書中哈夫曼樹及其應用後對書中虛擬碼的實現和完善,採用哈夫曼靜態編碼的方式,通過對資料進行兩遍掃描,第一次統計出現的字元頻次,進而構造哈夫曼樹,第二遍掃描資料根據得到的哈夫曼樹對資料進行編碼。

對於其中的加密編碼只是簡單的將使用者輸入的密碼用特殊的標識位拼接成字串加入需要統計的資料,可以在擁有哈夫曼編碼表以及加密後的文字情況下進行解密

哈夫曼樹

給定n個權值作為n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。

哈夫曼樹的構造過程

假設有n個權值,則構造出的哈夫曼樹有n個葉子結點。 n個權值分別設為 w1、w2、…、wn,則哈夫曼樹的構造規則為: (1)
將w1、w2、…,wn看成是有n 棵樹的森林(每棵樹僅有一個結點); (2)
在森林中選出兩個根結點的權值最小的樹合併,作為一棵新樹的左、右子樹,且新樹的根結點權值為其左、右子樹根結點權值之和;
(3)從森林中刪除選取的兩棵樹,並將新樹加入森林; (4)重複(2)、(3)步,直到森林中只剩一棵樹為止,該樹即為所求得的哈夫曼樹

在構造哈夫曼樹時首先選擇權小的,這樣保證權大的離根較近,這種生成演算法是一種典型的貪心法

哈夫曼編碼思想

為了使壓縮後的資料檔案儘可能短,可採用不定長編碼。而為了在對壓縮檔案進行解碼時不產生二義性,確保正確解碼應該採用字首編碼的形式(即要求一個字元的編碼不能是另一個字元編碼的字首)。而哈夫曼編碼是最優字首編碼。

例如:有一個數據序列ABACCDAA則編碼為A(0),B(10),C(110),(D111),壓縮後為010011011011100。

部分實現程式碼

//哈夫曼樹儲存表示 
typedef struct {
    int  weight;  //節點的權值 
    int parent,lchild,rchild;  //節點的雙親,左孩子,右孩子的下標 
} HTNode,*HuffmanTree;

//儲存資料掃瞄統計結果 
typedef struct { char* data; int* num; int length; } TNode; //儲存檔案讀入哈夫曼編碼結果 typedef struct { char *data; char** HM; } Code; //儲存哈夫曼編碼結果 typedef char** HuffmanCode; //選取節點構造哈夫曼樹 void Select(HuffmanTree &HT,int m,int& s1,int& s2) { int k,j,n,min=32767; for(k=1; k<=m; k++) { if(HT[k].parent==0) if(HT[k].weight<=min) { j=k; min=HT[k].weight; } } s1=j; HT[j].parent=1; min=32767; for(k=1; k<=m; k++) { if(HT[k].parent==0) if(HT[k].weight<=min) { n=k; min=HT[k].weight; } } s2=n; } //構造哈夫曼樹 void CreateHuffmanTree (HuffmanTree &HT,TNode T,int length) { int m,i,s1,s2; //初始化 if(length<=1) return; m=2*length-1; HT=new HTNode[m+1]; for(i=1; i<=m; ++i) { HT[i].parent=0; HT[i].lchild=0; HT[i].rchild=0; } for(i=1; i<=length; ++i) HT[i].weight=T.num[i-1]; //通過n-1次的選擇,刪除,合併來建立哈夫曼樹 for(i=length+1; i<=m; i++) { Select(HT,i-1,s1,s2); HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1; HT[i].rchild=s2; HT[i].weight=HT[s1].weight+HT[s2].weight; } } //從葉子到根逆向求每個字元的哈夫曼編碼,儲存在編碼表HC中 void CreatHuffmanCode (HuffmanTree HT,HuffmanCode &HC,int n) { int i,f,c,start; HC=new char*[n+1]; char* cd=new char[n]; cd[n-1]='\0'; for(i=1; i<=n; i++) { start=n-1; c=i; f=HT[i].parent; while(f!=0) { --start; if(HT[f].lchild==c) cd[start]='0'; else cd[start]='1'; c=f; f=HT[f].parent; } HC[i]=new char[n-start]; strcpy(HC[i],&cd[start]); } delete cd; }

參考 資料結構:C語言版/嚴蔚敏,李冬梅,吳偉民編