min1=T[i].weight; } *s2=j; }6
4.1.2 统计字符串中字符的种类以及各类字符的个数
假设电子文件字符串全是大写字母,那么该算法的实现思想是:
先定义一个含有26个元素的临时整型数组,用来存储各种字母出现的次数。应为大写字母的ASCII码与整数1~26个元素之间相差,因此在算法中使用字母减去作为统计数组的下标对号入座,无须循环判断来实现,从而提高了效率;另外,要求出电文字符串中有多少种字符,并保存这些字符以供编码时使用。统计和保存都比较容易,用一个循环来判断先前统计好的各类字符个数的数组元素是否为零,若不为零,则将其值存入一个数组对应的元素中,同时将其对应的字符也存入另一个数组元素中。具体实现如下:
int jsq(char *s,int cnt[],char str[]) {
//统计各字符串中各种字母的个数以及字符的种类 char *p; int i,j,k; int temp[27]; for(i=1;i<=26;i++) temp[i]=0;
7
for(p=s;*p!='\\0';p++) {//统计各种字符个数 if(*p>='A' && *p<='Z') { k=*p-; temp[k]++; } } j=0;
for(i=1,j=0;i<=26;i++)//统计有多少种字符 if(temp[i]!=0) { j++;
str[j]=i+;//将对应的数组送到数组中 cnt[j]=temp[i];//存入对应数组的权值 } return j; }
4.1.3构造赫夫曼树
void
ChuffmanTree(HuffmanTree
HT,HuffmanCode
cnt[],char str[]) {//构造赫夫曼树HT int i,s1,s2;
8
HC,int
for(i=1;i<=2*num-1;i++)//初始化HT,左右孩子,双亲,权值都为0 { HT[i].lchild=0; HT[i].rchild=0; HT[i].parent=0; HT[i].weight=0; }
for(i=1;i<=num;i++)//输入num个叶节点的权值 HT[i].weight=cnt[i];
for(i=num+1;i<=2*num-1;i++)//从numd后面开始新建结点存放新生成的父节点 {
select(HT,i-1,&s1,&s2);//在HT[1……i-1]中选择parent为0且权值最小的两个根结点,其序号分别为s1和s2
HT[s1].parent=i;HT[s2].parent=i;//将s1和s2的parent赋值 HT[i].lchild=s1; HT[i].rchild=s2;//新结点的左右孩子 HT[i].weight=HT[s1].weight+ HT[s2].weight;//新结点的权值 }
for(i=0;i<=num;i++)//输入字符集中的字符 HC[i].ch=str[i]; i=1;
while(i<=num)
printf(\"字符 %c,次数为: %d\\n\}
9
4.2赫夫曼编码
要求电文的赫夫曼编码,必须先定义赫夫曼编码类型,根据设计要
求和实际需要定义的类型如下:
typedef struct{ char ch; char bits[n+1]; int start ; }CodeNode;
typedef CodeNode HuffmanCode[n];
4.2.1赫夫曼编码算法
void HuffmanEncoding(HuffmanTree HT,HuffmanCode HC) {//根据赫夫曼树HT 求赫夫曼编码表HC int c,p,i; char cd[n];
int start; cd[num]='\\0'; for(i=1;i<=num;i++) {
start=num;
10
c=i;
while((p=HT[c].parent)>0)//直至上诉到ht[c]是树根为止 {//若HT[c]是HT[p]的孩子,则生成0;否则生成代码1 cd[--start]=(HT[p].lchild= =c)? '0':'1' : c=p;
}//end of while
strcpy(HC[i].bits,&cd[start]); HC[i].len=num-start; } }
4.2.2建立正文的编码文件
建立编码文件的基本思想是:将要编码的字符串中的字符逐一与
预先生成赫夫曼树时保保存的 字符编码对照表进行比较,找到之后,对该字符的编码写入代码文件,直至所有字符处理完毕为止。 具体算法如下:
viod coding(huffmanCode HC,char *str) { int i,j; FILE *fp;
11
fp =fopen(“codefile.tex”,”w”);
while(*str){//对电文中字符逐一生成编码并写入文件 for(i=1;i<=num;i++) if(HC[i].ch= =*str){ for(j=0;j<=HC[i].len;j++) f putc (HC[i].bits[j],fp); break; } str++; }
fclose(fp); }
4.3代码文件的译码
译码的基本思想是:读文件中编码,并与生成的赫夫曼编码表比
较,遇到相等时,即取出其对应的 字符存入一个新串中。 Char * decode (HuffmanCode HC) {//代码文件codefile.tex 译码 FILE *fp; char str[254]; char *p;
static char cd[n+1] int i,j,k=0,cjs;
12
fp=fopen(“codefile.tex”,”r”); while(!feof(fp)) {cjs=0;
for(i=0;i<=num&&cjs==0&&!feof(fp);i++) { cd[i]=‘ ;’ cd[i+1]='\\0'; cd[i]=fgetc(fp); for(j=1;j<=num;j++)
if(strcmp(HC[i].bits,cd)= =0) {
str[k]=HC[j].ch;k++; cjs=1;break; } } str[k]='\\0'; ;p=str; return p; }
13
五、运行与测试
运行结果为
六、总结与心得
本次编写过程中出现了较多的问题,比如开始对赫夫曼树的理解
不是很清楚,导致在编写过程中某些代码错误而没能及时修改,在最后进行修改时遇到了较多的麻烦。但是经过这次对赫夫曼树的学习后,我了解到赫夫曼编码(Huffman Coding)是一种编码方式,以赫夫曼树—即最优二叉树,带权路径长度最小的二叉树,经常应用于数据的无损耗压缩。总之受益匪浅。
14