📄 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")类似的函数,这个函数好象没见过,如果c或c++那应该用什么函数?后缀wxl是什么意思啊,还有后面的wb也无法理解打开另外一个文件怎么用fopen("c:\\hfmtree.wxl","wb")类似的函数,这个函数好象没见过,如果c或c++那应该用什么函数?后缀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的结点,用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){
/*数组c和w存放了n个字符及其概率*/
int i,s1,s2;
for(i=1;i<=n;i++){/*根据n个权值构造n棵只有根结点的二叉树*/
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++){/*构造霍夫曼树*/
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个叶子结点在霍夫曼树HT中的下标为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)对字符逐个进行编码*/
/*最后把编码存储在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个叶子结点的最优二叉树(存储在数组HT中)进行译码,叶子的下标为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","欢迎使用霍夫曼编码系统。");
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 编码简介
在一般的数据结构的书中,树的那章后面,著者一般都会介绍一下哈夫曼(HUFFMAN)树和哈夫曼编码。哈夫曼编码是哈夫曼树的一个应用。哈夫曼编码应用广泛,如JPEG中就应用了哈夫曼编码。
首先介绍什么是哈夫曼树。哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的带权路径长度记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。
哈夫曼在上世纪五十年代初就提出这种编码时,根据字符出现的概率来构造平均长度最短的编码。它是一种变长的编码。在编码中,若各码字长度严格按照码字所对应符号出现概率的大小的逆序排列,则编码的平均长度是最小的。(注:码字即为符号经哈夫曼编码后得到的编码,其长度是因符号出现的概率而不同,所以说哈夫曼编码是变长的编码。)
然而怎样构造一棵哈夫曼树呢?最具有一般规律的构造方法就是哈夫曼算法。一般的数据结构的书中都可以找到其描述:
一、对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F={T1,T2,T3,...,Ti,...,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。(为方便在计算机上实现算法,一般还要求以Ti的权值Wi的升序排列。)
二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止。
用C语言实现上述算法,可用静态的二叉树或动态的二叉树。若用动态的二叉树可用以下数据结构: struct tree{
float weight; /*权值*/
union{
char leaf; /*叶结点信息字符*/
struct tree *left; /*树的左结点*/
};
struct tree *right; /*树的右结点*/
};
struct forest{ /*F集合,以链表形式表示*/
struct tree *ti; /* F中的树*/
struct forest *next; /* 下一个结点*/
};
例:若字母A,B,Z,C出现的概率为:0.71,0.54,0.28,0.43;则相应的权值为:75,54,28,43。
构造好哈夫曼树后,就可根据哈夫曼树进行编码。例如:上面的字符根据其出现的概率作为权值构造一棵哈夫曼树后,经哈夫曼编码得到的对应的码值。只要使用同一棵哈夫曼树,就可把编码还原成原来那组字符。显然哈夫曼编码是前缀编码,即任一个字符的编码都不是另一个字符的编码的前缀,否则,编码就不能进行翻译。例如:a,b,c,d的编码为:0,10,101,11,对于编码串:1010就可翻译为bb或ca,因为b的编码是c的编码的前缀。刚才进行哈夫曼编码的规则是从根结点到叶结点(包含原信息)的路径,向左孩子前进编码为0,向右孩子前进编码为1,当然你也可以反过来规定。
这种编码方法是静态的哈夫曼编码,它对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(2^8=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0--2^32-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。 静态哈夫曼编码方法有一些缺点:一、对于过短的文件进行编码的意义不大,因为光以4BYTES的长度存储哈夫曼树的信息就需1024Bytes的存储空间;二、进行哈夫曼编码,存储编码信息时,若用与通讯网络,就会引起较大的延时;三、对较大的文件进行编码时,频繁的磁盘读写访问会降低数据编码的速度。
因此,后来有人提出了一种动态的哈夫曼编码方法。动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行的,编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。动态哈夫曼编码比静态哈夫曼编码复杂的多,有兴趣的读者可参考有关数据结构与算法的书籍。
前面提到的JPEG中用到了哈夫曼编码,并不是说JPEG就只用哈夫曼编码就可以了,而是一幅图片经过多个步骤后得到它的一列数值,对这些数值进行哈夫曼编码,以便存储或传输。哈夫曼编码方法比较易懂,大家可以根据它的编码方法,自己编写哈夫曼编码和解码的程序。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -