?? hemanhu.txt
字號:
exit(0);
}
if((fp2=fopen("c:\\textfile.txt","wb"))==NULL)
{
puts("文件打開錯誤!");
getchar();
exit(0);
}
if((fp3=fopen("c:\\codefile.wxl","rb"))==NULL)
{
puts("文件打開錯誤!");
getchar();
exit(0);
}
while((ch3=fgetc(fp3))!=EOF)
{
t3=0;
while(ch3!='@')
{
temp_3[t3++]=ch3;
ch3=fgetc(fp3);
}
temp_3[t3]='\0';
while((ch1=fgetc(fp1))!=EOF)
{
if(isalpha(ch1))
{
ch2=ch1;
t1=0;
while((ch1=fgetc(fp1))!=')')
{
temp_1[t1++]=ch1;
}
temp_1[t1]='\0';
if(strcmp(temp_1,temp_3)==0)
{
fputc(ch2,fp2);
rewind(fp1);
break;
}
}
}
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
}
void disp(void)
{
FILE *fp1,*fp2;
char ch1,ch2;
char tmp[20];
int t;
if((fp1=fopen("c:\\hfmtree.wxl","rb"))==NULL)
{
puts("文件打開錯誤!");
getchar();
exit(0);
}
if((fp2=fopen("c:\\hfmcode.txt","wb"))==NULL)
{
puts("文件打開錯誤!");
getchar();
exit(0);
}
while((ch1=fgetc(fp1))!=EOF)
{
if(ch1=='(')
{
t=0;
ch1=fgetc(fp1);
ch2=ch1;
while((ch1=fgetc(fp1))!=')')
{
tmp[t++]=ch1;
}
tmp[t]='\0';
printf("%c-----%s\n",ch2,tmp);
fputc(ch2,fp2);
fputc('-',fp2);
fputc('-',fp2);
fputc('-',fp2);
fputs(tmp,fp2);
fputc('\n',fp2);
}
}
fclose(fp1);
fclose(fp2);
}
在建立樹完成之后,輸入"2",根本不能編碼,提示"文件打開錯誤",這是什么原因啊.還有,這個程序中,打開另外一個文件怎么用fopen("c:\\hfmtree.wxl","wb")類似的函數(shù),這個函數(shù)好象沒見過,如果c或c++那應(yīng)該用什么函數(shù)?后綴wxl是什么意思啊,還有后面的wb也無法理解打開另外一個文件怎么用fopen("c:\\hfmtree.wxl","wb")類似的函數(shù),這個函數(shù)好象沒見過,如果c或c++那應(yīng)該用什么函數(shù)?后綴wxl是什么意思啊,還有后面的wb也無法理解,還有后面的(ch1=fgetc(fp1))!=EOF中的EOF是什么意思呢,傷腦筋啊
2.
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAXLEAFNUM 50
typedef struct node{ /*Huffmantree node*/
char ch;
int weigth;
int parent;
int lchild,rchild;
}HuffmanTree[2*MAXLEAFNUM];
typedef char* HuffmanCode[MAXLEAFNUM+1]; /*string Huffmancoding*/
void select(HuffmanTree HT,int n,int *p1,int *p2){
/*在HT[1~n]中選擇weigth最小的且parent為0的結(jié)點,用p1,p2帶回*/
static int first=1;/*每次查找都從first開始*/
int k,i;
while(HT[first].parent!=0) first++; /*更新first*/
/*查找第一個*/
k=first;
for(i=first+1;i<=n;i++)
if(HT[i].parent==0&&HT[i].weigth<HT[k].weigth)
k=i;
if(k==first){
first++;
while(HT[first].parent!=0) first++;
}
*p1=k;
/*查找第二個*/
k=first;
for(i=first+1;i<=n;i++)
if(HT[i].parent==0&&HT[i].weigth<HT[k].weigth&&i!=*p1)
k=i;
if(k==first){
first++;
while(HT[first].parent!=0)first++;
}
*p2=k;
}/*select*/
void createHTree(HuffmanTree HT,char *c,int *w,int n){
/*數(shù)組c和w存放了n個字符及其概率*/
int i,s1,s2;
for(i=1;i<=n;i++){/*根據(jù)n個權(quán)值構(gòu)造n棵只有根結(jié)點的二叉樹*/
HT[i].ch=c[i-1];
HT[i].weigth=w[i-1];
HT[i].parent=HT[i].lchild=HT[i].rchild=0;
}
for(;i<2*n;i++){
HT[i].parent=HT[i].lchild=HT[i].rchild=0;
}
for(i=n+1;i<2*n;i++){/*構(gòu)造霍夫曼樹*/
select(HT,i-1,&s1,&s2);
HT[i].weigth=HT[s1].weigth+HT[s2].weigth;
HT[i].lchild=s1;HT[i].rchild=s2;
HT[s1].parent=HT[s2].parent=i;
}
}/*createHTree*/
void HuffmanCoding(HuffmanTree HT,HuffmanCode HC,int n){
/*n個葉子結(jié)點在霍夫曼樹HT中的下標(biāo)為1~n,第i(i<=i<=n)個葉子的編碼存放HC[i]中*/
char *cd;
int i,start,c,f;
if(n<=1) return ;
cd=(char*)malloc(n);
cd[n-1]='\0';
i=1;
while(i<=n){
start=n-1;
for(c=i,f=HT[c].parent;f!=0;c=f,f=HT[c].parent){
if(HT[f].lchild==c) cd[--start]='1';
else cd[--start]='0';
}
HC[i]=(char*)malloc(n-start);
strcpy(HC[i],&cd[start]);
i++;
}/*while*/
}/*HuffmanCoding*/
void Coding(HuffmanTree HT,HuffmanCode HC,int n,char **buff){
/*利用具有n個字符的霍夫曼編碼的編碼集(1~n)對字符逐個進(jìn)行編碼*/
/*最后把編碼存儲在buff 所指向的字符指針中*/
int i;
char c;
c=getchar();
for(i=1;HT[i].ch!=c&&i<=n;i++);
if(i>n){ printf("\7"); return;}
*buff=(char*)malloc(strlen(HC[i]));
strcpy(*buff,HC[i]);
c=getchar();
while(c!='\n'&&c!=EOF){
for(i=1;HT[i].ch!=c&&i<=n;i++);
if(i>n){ printf("\7"); return;}
strcat(*buff,HC[i]);
c=getchar();
}/*while*/
}/*Bucoding*/
void Decoding(HuffmanTree HT,int n,char* buff){
/*利用具有n個葉子結(jié)點的最優(yōu)二叉樹(存儲在數(shù)組HT中)進(jìn)行譯碼,葉子的下標(biāo)為1~n*/
/*buff指向原文的編碼序列*/
int p=2*n-1;
while(*buff){
if(*buff=='1') p=HT[p].lchild;
else p=HT[p].rchild;
if(p<=n){
printf("%c",HT[p].ch);
p=2*n-1;
}
buff++;
}/*while*/
}/*Decoding*/
void main(){
char c[]="abcdefghijklmnopqrstuvwxyz,. \n";
int w[30]={4,4,4,4,4,5,4,
4,3,3,2,2,3,3,
2,3,2,3,5,4,
3,2,3,2,2,3,
5,2,6,1};
int n=30;
int In,i;
char* buff;
HuffmanTree HT;
HuffmanCode HC;
createHTree(HT,c,w,n);
HuffmanCoding(HT,HC,n);
printf("%32s\n","歡迎使用霍夫曼編碼系統(tǒng)。");
printf("%13s\n%13s\n%17s\n","編碼請按1","解碼請按2","查看編碼請按3");
scanf("%d",&In);
switch(In){
case 1: flushall();Coding(HT,HC,n,&buff);break;
case 2:Decoding(HT,n,buff);break;
case 3: for(i=1;i<=n;i++){
printf("The string \"%c\" code is %s\n",c[i-1],HC[i]);
}
break;
}
while(*buff)
printf("%c",*buff++);
printf("\n");
}
Huffman 編碼簡介
在一般的數(shù)據(jù)結(jié)構(gòu)的書中,樹的那章后面,著者一般都會介紹一下哈夫曼(HUFFMAN)樹和哈夫曼編碼。哈夫曼編碼是哈夫曼樹的一個應(yīng)用。哈夫曼編碼應(yīng)用廣泛,如JPEG中就應(yīng)用了哈夫曼編碼。
首先介紹什么是哈夫曼樹。哈夫曼樹又稱最優(yōu)二叉樹,是一種帶權(quán)路徑長度最短的二叉樹。所謂樹的帶權(quán)路徑長度,就是樹中所有的葉結(jié)點的權(quán)值乘上其到根結(jié)點的路徑長度(若根結(jié)點為0層,葉結(jié)點到根結(jié)點的路徑長度為葉結(jié)點的層數(shù))。樹的帶權(quán)路徑長度記為WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N個權(quán)值Wi(i=1,2,...n)構(gòu)成一棵有N個葉結(jié)點的二叉樹,相應(yīng)的葉結(jié)點的路徑長度為Li(i=1,2,...n)。可以證明哈夫曼樹的WPL是最小的。
哈夫曼在上世紀(jì)五十年代初就提出這種編碼時,根據(jù)字符出現(xiàn)的概率來構(gòu)造平均長度最短的編碼。它是一種變長的編碼。在編碼中,若各碼字長度嚴(yán)格按照碼字所對應(yīng)符號出現(xiàn)概率的大小的逆序排列,則編碼的平均長度是最小的。(注:碼字即為符號經(jīng)哈夫曼編碼后得到的編碼,其長度是因符號出現(xiàn)的概率而不同,所以說哈夫曼編碼是變長的編碼。)
然而怎樣構(gòu)造一棵哈夫曼樹呢?最具有一般規(guī)律的構(gòu)造方法就是哈夫曼算法。一般的數(shù)據(jù)結(jié)構(gòu)的書中都可以找到其描述:
一、對給定的n個權(quán)值{W1,W2,W3,...,Wi,...,Wn}構(gòu)成n棵二叉樹的初始集合F={T1,T2,T3,...,Ti,...,Tn},其中每棵二叉樹Ti中只有一個權(quán)值為Wi的根結(jié)點,它的左右子樹均為空。(為方便在計算機(jī)上實現(xiàn)算法,一般還要求以Ti的權(quán)值Wi的升序排列。)
二、在F中選取兩棵根結(jié)點權(quán)值最小的樹作為新構(gòu)造的二叉樹的左右子樹,新二叉樹的根結(jié)點的權(quán)值為其左右子樹的根結(jié)點的權(quán)值之和。
三、從F中刪除這兩棵樹,并把這棵新的二叉樹同樣以升序排列加入到集合F中。
四、重復(fù)二和三兩步,直到集合F中只有一棵二叉樹為止。
用C語言實現(xiàn)上述算法,可用靜態(tài)的二叉樹或動態(tài)的二叉樹。若用動態(tài)的二叉樹可用以下數(shù)據(jù)結(jié)構(gòu): struct tree{
float weight; /*權(quán)值*/
union{
char leaf; /*葉結(jié)點信息字符*/
struct tree *left; /*樹的左結(jié)點*/
};
struct tree *right; /*樹的右結(jié)點*/
};
struct forest{ /*F集合,以鏈表形式表示*/
struct tree *ti; /* F中的樹*/
struct forest *next; /* 下一個結(jié)點*/
};
例:若字母A,B,Z,C出現(xiàn)的概率為:0.71,0.54,0.28,0.43;則相應(yīng)的權(quán)值為:75,54,28,43。
構(gòu)造好哈夫曼樹后,就可根據(jù)哈夫曼樹進(jìn)行編碼。例如:上面的字符根據(jù)其出現(xiàn)的概率作為權(quán)值構(gòu)造一棵哈夫曼樹后,經(jīng)哈夫曼編碼得到的對應(yīng)的碼值。只要使用同一棵哈夫曼樹,就可把編碼還原成原來那組字符。顯然哈夫曼編碼是前綴編碼,即任一個字符的編碼都不是另一個字符的編碼的前綴,否則,編碼就不能進(jìn)行翻譯。例如:a,b,c,d的編碼為:0,10,101,11,對于編碼串:1010就可翻譯為bb或ca,因為b的編碼是c的編碼的前綴。剛才進(jìn)行哈夫曼編碼的規(guī)則是從根結(jié)點到葉結(jié)點(包含原信息)的路徑,向左孩子前進(jìn)編碼為0,向右孩子前進(jìn)編碼為1,當(dāng)然你也可以反過來規(guī)定。
這種編碼方法是靜態(tài)的哈夫曼編碼,它對需要編碼的數(shù)據(jù)進(jìn)行兩遍掃描:第一遍統(tǒng)計原數(shù)據(jù)中各字符出現(xiàn)的頻率,利用得到的頻率值創(chuàng)建哈夫曼樹,并必須把樹的信息保存起來,即把字符0-255(2^8=256)的頻率值以2-4BYTES的長度順序存儲起來,(用4Bytes的長度存儲頻率值,頻率值的表示范圍為0--2^32-1,這已足夠表示大文件中字符出現(xiàn)的頻率了)以便解壓時創(chuàng)建同樣的哈夫曼樹進(jìn)行解壓;第二遍則根據(jù)第一遍掃描得到的哈夫曼樹進(jìn)行編碼,并把編碼后得到的碼字存儲起來。 靜態(tài)哈夫曼編碼方法有一些缺點:一、對于過短的文件進(jìn)行編碼的意義不大,因為光以4BYTES的長度存儲哈夫曼樹的信息就需1024Bytes的存儲空間;二、進(jìn)行哈夫曼編碼,存儲編碼信息時,若用與通訊網(wǎng)絡(luò),就會引起較大的延時;三、對較大的文件進(jìn)行編碼時,頻繁的磁盤讀寫訪問會降低數(shù)據(jù)編碼的速度。
因此,后來有人提出了一種動態(tài)的哈夫曼編碼方法。動態(tài)哈夫曼編碼使用一棵動態(tài)變化的哈夫曼樹,對第t+1個字符的編碼是根據(jù)原始數(shù)據(jù)中前t個字符得到的哈夫曼樹來進(jìn)行的,編碼和解碼使用相同的初始哈夫曼樹,每處理完一個字符,編碼和解碼使用相同的方法修改哈夫曼樹,所以沒有必要為解碼而保存哈夫曼樹的信息。編碼和解碼一個字符所需的時間與該字符的編碼長度成正比,所以動態(tài)哈夫曼編碼可實時進(jìn)行。動態(tài)哈夫曼編碼比靜態(tài)哈夫曼編碼復(fù)雜的多,有興趣的讀者可參考有關(guān)數(shù)據(jù)結(jié)構(gòu)與算法的書籍。
前面提到的JPEG中用到了哈夫曼編碼,并不是說JPEG就只用哈夫曼編碼就可以了,而是一幅圖片經(jīng)過多個步驟后得到它的一列數(shù)值,對這些數(shù)值進(jìn)行哈夫曼編碼,以便存儲或傳輸。哈夫曼編碼方法比較易懂,大家可以根據(jù)它的編碼方法,自己編寫哈夫曼編碼和解碼的程序。
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -