基于C語(yǔ)言利用哈夫曼樹(shù)實(shí)現(xiàn)文件壓縮的問(wèn)題
一、哈夫曼樹(shù)
具有n個(gè)權(quán)值的n個(gè)葉子結(jié)點(diǎn),構(gòu)造出一個(gè)二叉樹(shù),使得該樹(shù)的帶權(quán)路徑長(zhǎng)度(WPL)最小,則稱此二叉樹(shù)為最優(yōu)二叉樹(shù),也稱為哈夫曼樹(shù)(Huffman Tree)。
注意:哈夫曼樹(shù)是帶權(quán)路徑長(zhǎng)度最短的樹(shù),且權(quán)值越大的葉子結(jié)點(diǎn)離根結(jié)點(diǎn)越近。
二、哈夫曼編碼
哈夫曼編碼是一種編碼方式,又稱“霍夫曼編碼”,其是可變字長(zhǎng)的編碼(VCL)的一種,是由霍夫曼于1952年提出的一種編碼方式,有時(shí)被稱為最佳編碼,一般稱為Huffman編碼。
那么我們?yōu)槭裁匆褂霉蚵幋a進(jìn)行壓縮?
首先原因在于,傳統(tǒng)壓縮方式中,字符的編碼是以等字長(zhǎng)的方式進(jìn)行壓縮,相比于哈夫曼編碼的可變字長(zhǎng)而言壓縮率會(huì)降低。其次,傳統(tǒng)方式中可能會(huì)出現(xiàn)某些字符的編碼相同,存在二義性,相比于哈夫曼編碼的唯一性。可以大大提高正確性。
2.1 如何得到哈夫曼編碼
通過(guò)已經(jīng)構(gòu)造的哈夫曼樹(shù),結(jié)點(diǎn)左分支記為二進(jìn)制數(shù)“0”,結(jié)點(diǎn)右分支記為二進(jìn)制數(shù)“1”。從根結(jié)點(diǎn)向下到該葉子結(jié)點(diǎn)路途經(jīng)過(guò)的數(shù)字拼接成為的編碼即為該字符的哈夫曼編碼?,F(xiàn)通過(guò)以下圖例進(jìn)行演示。

此時(shí)各字符對(duì)應(yīng)的哈夫曼編碼可表示為:
A:00 B:01 C:1
三、哈夫曼樹(shù)實(shí)現(xiàn)文件壓縮基本步驟
3.1 基本實(shí)現(xiàn)流程

3.2 使用文件輸入流讀取需壓縮的文件,并判斷該文件是否存在
存在后執(zhí)行接下來(lái)的步驟,不存在提示用戶,效果圖如下

3.3 讀取,統(tǒng)計(jì)字符
用戶輸入正確文件路徑且存在時(shí)使用文件輸入流讀取文件,將文件放于一個(gè)字符數(shù)組中(注意字符數(shù)組開(kāi)辟空間應(yīng)足夠大)。統(tǒng)計(jì)出現(xiàn)的字符及其出現(xiàn)的次數(shù)。
char ch;
while(feof(fp) == 0){
fscanf(fp,"%c",&ch);
str[ch]++;
}
printf("\n");
for(i = 0; i < 1024; i++){
if(str[i] != 0){
count[n++] = i;
}
}
3.4 構(gòu)造哈夫曼樹(shù)
注:由于后續(xù)會(huì)使用此哈夫曼編碼,為了方便獲取,這里將求出的哈夫曼編碼放置于一個(gè)二維數(shù)組中。
CreateHT(ht,node);
char strarr[256][256];
int aa,bb;
for(aa=0; aa<256;aa++)
for(bb=0;bb<256;bb++)
strarr[aa][bb]=-1;
for(k = 0;k < node;k++){
huffmanCode(ht,k,strarr[k]);
}
void CreateHT(HTNode *ht, int n){
int i, j, lnode, rnode n1, n2;
for(i = n; i < 2*n-1; i++){
lnode = 65432; rnode = lnode;
n1 = 0; n2 = 0;
for(j = 0; j < i; j++){
if(ht[j].parent == -1){
if(ht[j].weight <lnode){
rnode = lnode;
n2 =n1;
lnode = ht[j].weight;
n1 = j;
} else if(ht[j].weight < rnode){
rnode = ht[j].weight;
n2 = j;
}
}
}
ht[n1].parent = i;
ht[n2].parent = i;
ht[i].weight = lnode+rnode;
ht[i].parent = -1;
ht[i].lchild = n1;
ht[i].rchild = n2;
}
}
3.5 根據(jù)構(gòu)造哈夫曼樹(shù)得到哈夫曼編碼
此時(shí)我們使用3.3中統(tǒng)計(jì)的字符作為葉子結(jié)點(diǎn),其出現(xiàn)次數(shù)作為權(quán)值,構(gòu)造哈夫曼樹(shù)求出哈夫曼編碼。下面以此段英文為例,得到每個(gè)字符對(duì)應(yīng)哈夫曼編碼。
You can take away our money,house,car,or even our clothes and we can surive.But if our health was taken away,we would surely die.That is why we always try to eat in a healthy way and excise regularly..

3.6 替換字符為其哈夫曼編碼
用戶輸入壓縮至的文件路徑,將替換好的0 1數(shù)字字符數(shù)組寫(xiě)入該文件。由于C語(yǔ)言沒(méi)有基于字符串的操做,所以我們替換時(shí)只能通過(guò)對(duì)字符數(shù)組進(jìn)行操作,涉及到了被替換字符串長(zhǎng)度與替換字符串長(zhǎng)度的問(wèn)題,以及字符數(shù)組的長(zhǎng)度是否充足。由于此次替換被替換字符串只是一個(gè)字符,且字符數(shù)組開(kāi)辟空間足夠大。所以只考慮替換字符串長(zhǎng)度,即字符對(duì)應(yīng)哈夫曼編碼的長(zhǎng)度,在這里使用一個(gè)方法來(lái)進(jìn)行替換。
void replace(char oldstr[], char key[], char rep[]){
int oldstrLen, lengthOfKey, lengthOfRep, i, j , flag;
char tmp[1000];
oldstrLen = strlen(oldstr);
lengthOfKey = strlen(key);
lengthOfRep = strlen(rep);
for( i = 0; i <= oldstrLen - lengthOfKey; i++){
flag = 1;
for(j = 0; j < lengthOfKey; j++){
if(oldstr[i + j] != key[j]){
flag = 0;
break;
}
}
if(flag){
strcpy(tmp, oldstr);
strcpy(&tmp[i], rep);
strcpy(&tmp[i + lengthOfRep], &oldstr[i + lengthOfKey]);
strcpy(oldstr, tmp);
i += lengthOfRep - 1;
oldstrLen = strlen(oldstr);
}
}
}
替換完成后的字符數(shù)組使用輸出流寫(xiě)入用戶輸入文件路徑。
3.7 進(jìn)制轉(zhuǎn)換
將文件中每八位二進(jìn)制數(shù)轉(zhuǎn)換為十進(jìn)制數(shù),再轉(zhuǎn)換為其對(duì)應(yīng)的ASCII碼值,最后不足八位的二進(jìn)制數(shù)以0補(bǔ)齊,實(shí)現(xiàn)文件壓縮。
3.7.1 進(jìn)制轉(zhuǎn)換方法
int retTen(char str[]){
int ten = 0;
int t = 0;
for(t = 0;t < strlen(str);t++){
ten += (str[t]-'0') * pow(2,strlen(str) - t - 1);
}
return ten;
}
3.7.2 實(shí)現(xiàn)效果


3.8 計(jì)算壓縮率
最后為驗(yàn)證是否實(shí)現(xiàn)壓縮,可通過(guò)計(jì)算文件壓縮率來(lái)驗(yàn)證。其中,壓縮率=原文件字節(jié)大小/壓縮后文件大小。

int oldFileSize=getFileSize(path);
int newFileSize=getFileSize(Gopath);
printf("\n");
printf("壓縮成功!,文件已壓縮至%s\n",Gopath);
printf("該文件壓縮率為:%.2lf\n",(double)newFileSize/(double)oldFileSize);
注:注意計(jì)算壓縮率時(shí)的類(lèi)型轉(zhuǎn)換
到此這篇關(guān)于基于C語(yǔ)言利用哈夫曼樹(shù)實(shí)現(xiàn)文件壓縮的文章就介紹到這了,更多相關(guān)C語(yǔ)言實(shí)現(xiàn)文件壓縮內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言 常量,變量及數(shù)據(jù)詳細(xì)介紹
這篇文章主要介紹了C語(yǔ)言 常量,變量及數(shù)據(jù)詳解的相關(guān)資料,需要的朋友可以參考下2016-10-10
C++實(shí)現(xiàn)讀寫(xiě)文件的示例代碼
這篇文章主要介紹了C++實(shí)現(xiàn)讀寫(xiě)文件的示例代碼,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下2020-08-08
C語(yǔ)言實(shí)現(xiàn)字母大小寫(xiě)轉(zhuǎn)換的方法
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)字母大小寫(xiě)轉(zhuǎn)換的方法,涉及C語(yǔ)言字符串的遍歷與轉(zhuǎn)換技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-07-07
詳解C++中虛析構(gòu)函數(shù)的作用及其原理分析
這篇文章主要介紹了C++中虛析構(gòu)函數(shù)的作用及其原理分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
C++實(shí)現(xiàn)寢室衛(wèi)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)寢室衛(wèi)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
C/C++實(shí)現(xiàn)對(duì)STORM運(yùn)行信息查看及控制的方法
這篇文章主要介紹了C/C++實(shí)現(xiàn)對(duì)STORM運(yùn)行信息查看及控制的方法,需要的朋友可以參考下2014-07-07

