数据结构总结9——树3——哈夫曼编码的实现 by DexterYan

一、基础知识

二、代码要求

任意给定一个由26个大写英文字母组成的字符串,能对字符串中所有可能出现的字母进行哈夫曼编码(2学时)

三、代码实现

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXBIT 10			//每个字符编码的最大长度 
#define MAXVALUE 1000		//哈夫曼树中叶子结点的最大权值 
#define MAXSIZE 26			//电文中出现字符种类的最大值(26个英文字母) 

typedef struct HNode		//哈夫曼树的结点结构 
{
	int weight;//权值 
	int parent,lchild,rchild;
}HNode,*HTree;

typedef char** HCode;//用来指向哈夫曼编码 

/*统计电文中的字符种类以及每种字符出现的次数*/
int Count(char *s,int cnt[],char str[])//*s指向电文字符串,cnt存储电文中字符出现的次数,str存储电文中出现的字符 
{
	char *p;
	int i,j,k;
	int temp[26];
	for(i=0;i<26;i++)
	{
		temp[i]=0;//临时数组,用于统计每个字符出现的次数 
	}
	for(p=s; *p!='\0'; p++)
		if(*p>='A' && *p<='Z')
		{
			k=(*p)-65;
			temp[k]++;
		}
	for(i=0,j=0;i<26;i++)//统计电文中出现的字符 
		if(temp[i]!=0)
		{
			str[j]=i+65;//将其存入字符数组 
			cnt[j]=temp[i];
			j++;
		}
	str[j]='\0';
	return j;
}

/*构造哈弗曼树并生成叶子节点的前缀编码*/
/*思路:1、先根据给定n的权值,构造n课二叉树的集合F,没有孩子
2、在这中间选择两个权值最小的作为左右子树构造一颗新的二叉树,新权值为和
3、并删掉刚才那两棵树,并把新的加入到数组F中
4、重复2,3,最后所得即为哈夫曼树*/
void HuffmanCoding(HTree *HT,HCode *HC,int *w,int n)
{//w存储n个字符的权值,构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC 
	int m;//结点个数 
	int m1,m2,x1,x2;//m1,m2存储最小权值,x1,x2存储对应的下标 
	int i,j,start;
	char *cd;
	int c,f;
	HNode *p;
	if(n<=1)
		return;
	m=2*n-1;/*下边为构造哈夫曼树*/
	*HT=(HNode *)malloc(m*sizeof(HNode));
	for(p=*HT,i=0;i<n;++i,++p,++w)//初始化叶子结点信息 
	{//
		p->weight=*w;
		p->lchild=-1;
		p->rchild=-1;
		p->parent=-1;
	}
	for(;i<m;++i,++p)//初始化分支结点信息 
	{
		p->weight=0;
		p->lchild=-1;
		p->rchild=-1;
		p->parent=-1;
	}
	for(i=n;i<m;++i)//构造哈夫曼树 
	{
		m1=m2=MAXVALUE;//先都设为最大值,然后进行不断更新迭代为最小值 
		x1=x2=0;
		for(j=0;j<i;++j)
		{
			if((*HT)[j].parent==-1&&(*HT)[j].weight<m1)
			{
				m2=m1;
				x2=x1;
				m1=(*HT)[j].weight;
				x1=j;
			}
			else if((*HT)[j].parent==-1&&(*HT)[j].weight<m2)
			{
				m2=(*HT)[j].weight;
				x2=j;
			}
		}
		/*合并成一棵新的子树*/
		(*HT)[x1].parent=i;
		(*HT)[x2].parent=i;
		(*HT)[i].lchild=x1;
		(*HT)[i].rchild=x2;
		(*HT)[i].weight=m1+m2;
	}
	/*生成字符的前缀编码*/
/*
分解电文中的字符串,从哈夫曼树的根节点出发, 根据要找的字符寻找左右孩子,
并为路径置标志,然后通过置的0,1标志来写出编码 
*/
	*HC=(HCode)malloc(n*sizeof(char *));
	cd=(char *)malloc(n*sizeof(char));
	cd[n-1]='\0';
	for(i=0;i<n;++i)
	{
		start=n-1;
		for(c=i,f=(*HT)[i].parent; f!=-1; c=f,f=(*HT)[f].parent)
			if((*HT)[f].lchild==c)
				cd[--start]='0';
			else
				cd[--start]='1';
			(*HC)[i]=(char *)malloc((n-start)*sizeof(char));
			strcpy((*HC)[i],&cd[start]);
	}
	free(cd);
}
/*
void Coding(HCode HC,char *s,char str[])/*s指向电文字符串,str存储电文中出现的字符,HC字符的哈夫曼编码表*/
/*{
	int i,j;
	char *cp;
	FILE *fp;
	fp=fopen("\\codefile.txt","w");
	while(*s)
	{
		for(i=0; i<MAXSIZE; i++)
		{
			if(str[i]==*s)
			{
				for(j=0,cp=HC[i]; j<strlen(HC[i]); j++,cp++)
					fputc(*cp,fp);
				break;
			}
			s++;
		}
	}
	fclose(fp);
}*/
/*输出电文中每个字符出现的次数及其前缀编码*/
void Print(HCode HC,char str[],int cn[],int n)
{
	int i;
	for(i=0; i<n; i++)
	{
		printf("%c出现%d次,编码是:",str[i],cn[i]);
		puts(HC[i]);
		putchar('\n');
	}
	return;
}

/*对编码进行译码,恢复电文安全*/      //有问题!!!尚未修改!!! 
/*char *Decode(HCode HC,char str[],int num)
{
	FILE *fp;
	char s[254];
	char *p;
	static char cd[MAXBIT+1];
	int i, j, k=0, cjs;
	fp=fopen("\\codefile.txt","r");
	while(!feof(fp))
	{
		cjs=0;
		for(i=0; i<MAXSIZE && cjs==0 && !feof(fp); i++)
		{
			cd[i]=' ';
			cd[i+1]='\0';
			cd[i]=fgetc(fp);
			for(j=0; j<num; j++)
			{
				if(strcmp(HC[j],cd)==0)
				{
					s[k]=str[j];
					k++;
					cjs=1;
					break;
				}
			}
		}
	}
	s[k]='\0';
	p=s;
	return p;
}
*/
/*主函数*/
int main()
{
	char st[254],*s,str[26];
	int cn[26];
	int num;
	HNode *HT;
	HCode HC;
	printf("输入需要编码的字符串(假设均为大写字母):\n");
	gets(st);
	num=Count(st,cn,str);
	HuffmanCoding(&HT,&HC,cn,num);
	Print(HC,str,cn,num);
//	Coding(HC,st,str);
	printf("\n");
//	s=Decode(HC,str,num);
//	printf("%s\n",s);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41259302/article/details/90523069