哈夫曼建树、编码、解码加强版

直接上代码,注释详解
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//
/*定义赫夫曼树结点的结构体变量,存放结点的权值、字符、双亲、坐孩子和右孩子*/
typedef struct
{   
	int weight;   
    char ch;                 //增加一个域用于存放该节点的字符
    int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode; //指向赫夫曼编码的指针

//
/*本程序用到的函数原型*/
void welcome();    //打印操作选择界面
void HuffmanCoding(HuffmanTree &,char *,int *,int);//建立赫夫曼树的算法
void select(HuffmanTree HT,int j,int *s1,int *s2); //从目前已建好的赫夫曼树中选择parent为0且weight最小的两个结点
void Init(); //输入n个字符及其对应的权值,根据权值建立哈夫曼树
void Coding(); //编码
void Decoding(); //译码
void Print_code(); //打印译码好的代码文件
void Print_tree(); //以凹凸表形式打印哈夫曼树
int Read_tree(HuffmanTree &); //从文件中读入赫夫曼树
void find(HuffmanTree &HT,char *code,char *text,int i,int m);//译码时根据01字符串寻找相应叶子节点的递归算法
void Convert_tree(unsigned char T[100][100],int s,int *i,int j);//将内存中的赫夫曼树转换成凹凸表形式的赫夫曼树

HuffmanTree HT; //全局变量,指向存放赫夫曼树的存储空间
int n=0; //全局变量,存放赫夫曼树叶子结点的数目

int main()
{
	char select;
	while(1)
	{
		welcome();
		scanf("%c",&select);
		switch(select)
		{
		case 'i':
		case 'I':Init();break;
        case 'c':
        case 'C':Coding();break;
        case 'd':
        case 'D':Decoding();break;
        case 'p':
        case 'P':Print_code();break;
        case 't':
        case 'T':Print_tree();break;
        case 'e':
        case 'E':exit(1);
        default :printf("Input error!\n");
		}
        getchar();
	}
    return 0;
}

void welcome() //打印操作选择界面
{
	printf("*-----------------------------------------------------*\n");
    printf("|                你想干什么?                         |\n");
    printf("|-----------------------------------------------------|\n");
    printf("|                                                     |\n");
    printf("| I--------------------------建立哈夫曼树.            |\n");
    printf("| C--------------------------哈夫曼树编码.            |\n");
    printf("| D--------------------------哈夫曼树译码.            |\n");
    printf("| P--------------------------输出codefile文件.        |\n");
    printf("| T--------------------------输出哈夫曼树.            |\n");
	printf("| E--------------------------退出.                    |\n");
    printf("|                                                     |\n");
    printf("*-----------------------------------------------------*\n");
}

//
/*初始化函数,输入n个字符及其对应的权值,根据权值建立哈夫曼树,并将其存于文件hfmtree中*/
void Init() 
{
	FILE *fp;
    int i,n,w[52]; //w数组存放n个字符的权值
    char character[52]; //存放n个字符
    printf("\n输入字符个数 n:");
    scanf("%d",&n);        //输入字符集大小
    printf("输入%d个字符:\n",n);
    for (i=0;i<n;i++)
	{
		char b=getchar();
        scanf("%c",&character[i]);          //输入n个字符
	}
	printf("输入%d个对应的权值:\n",n);
	for(i=0;i<n;i++)
	{
		char b=getchar();
		scanf("%d",&w[i]);                  //输出n个对应权值
	}
    HuffmanCoding(HT,character,w,n);    //建立赫夫曼树
	if((fp=fopen("hfmtree.txt","w"))==NULL)
		printf("Open file hfmtree.txt error!\n");
    for (i=1;i<=2*n-1;i++)
	{
		if(fwrite(&HT[i],sizeof(HTNode),1,fp)!=1)   //将建立的赫夫曼树存入文件hfmtree.txt中
			printf("File write error!\n");
	}
	printf("\n建立赫夫曼树成功,已将其存于文件hfmtree.txt中\n");
	fclose(fp);
}

///
//建立赫夫曼树的算法///
void HuffmanCoding(HuffmanTree &HT,char *character,int *w,int n)
{
	int m,i,s1,s2;
	HuffmanTree p;
	if(n<=1) return;
	m=2*n-1;
	HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
	for(p=HT+1,i=1;i<=n;++i,++p,++character,++w)
	{
		p->ch=*character;p->weight=*w;p->parent=0;p->lchild=0;p->rchild=0;
	}
	for(;i<=m;++i,++p) 
	{
		p->ch=0;p->weight=0;p->parent=0;p->lchild=0;p->rchild=0;
	}
	for(i=n+1;i<=m;++i)
	{
		select(HT,i-1,&s1,&s2);
        HT[s1].parent=i;HT[s2].parent=i;
        HT[i].lchild=s1;HT[i].rchild=s2;
        HT[i].weight=HT[s1].weight+HT[s2].weight;
	}
}

///
/*从HT[1]到HT[j]中选择parent为0且weight最小的两个结点,用s1和s2返回其序号*/
void select(HuffmanTree HT,int j,int *s1,int *s2)
{
	int i;//找weight最小的结点
	for (i=1;i<=j;i++) 
		if (HT[i].parent==0)
		{
			*s1=i;break;
		}
		for (;i<=j;i++)
			if ((HT[i].parent==0)&&(HT[i].weight<HT[*s1].weight))
				*s1=i;
			HT[*s1].parent=1;//找weight次小的结点
			for (i=1;i<=j;i++)
				if (HT[i].parent==0)
				{
					*s2=i;break;
				}
				for (;i<=j;i++)
					if ((HT[i].parent==0)&&(i!=*s1)&&(HT[i].weight<HT[*s2].weight))
						*s2=i;
}

///
/*对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中*/
void Coding() 
{
	FILE *fp,*fw;
	int i,f,c,start;
	char *cd;
	HuffmanCode HC;
	if(n==0)
		n=Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
	
    /以下程序段求赫夫曼树中各叶子节点的字符对应的的编码,并存于HC指向的空间中
	{
		HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
		cd=(char *)malloc(n*sizeof(char));
		cd[n-1]='\0';
		for(i=1;i<=n;++i)
		{
			start=n-1;
			for(c=i,f=HT[i].parent;f!=0;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);
	}
	/
	if((fp=fopen("tobetrans.txt","rb"))==NULL)
		printf("Open file tobetrans.txt error!\n");
	if((fw=fopen("codefile.txt","wb+"))==NULL)
		printf("Open file codefile.txt error!\n");
	char temp;
	fscanf(fp,"%c",&temp); //从文件读入第一个字符
	while(!feof(fp))
	{
		//printf("aaaaaaaaa\n");
		for(i=1;i<=n;i++) 
			if(HT[i].ch==temp) break;    //在赫夫曼树中查找字符所在的位置
			for(int r=0;HC[i][r]!='\0';r++) //将字符对应的编码存入文件
			{
				fputc(HC[i][r],fw);
				//fwrite(&HC[i][r],sizeof(HTNode),1,fw);
				//fwrite(&HT[i],sizeof(HTNode),1,fp)
				//printf("bbbbbbb\n");
			}
			fscanf(fp,"%c",&temp);        //从文件读入下一个字符	
	}
	fclose(fw);
	fclose(fp);
	printf("\n对文件hfmtree.txt编码成功,结果已存入codefile.txt中。\n\n");
}


/
/*将文件codefile中的代码进行译码,结果存入文件textfile中*/
void Decoding() 
{
	FILE *fp,*fw;
	int m,i;
	char *code,*text,*p;
	
    if(n==0)
		n=Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
	
	if((fp=fopen("codefile.txt","rb"))==NULL)
		printf("Open file codefile.txt error!\n");
	if((fw=fopen("textfile.txt","wb+"))==NULL)
		printf("Open file textfile.txt error!\n");
	code=(char *)malloc(sizeof(char));
	fscanf(fp,"%c",code);        //从文件读入一个字符
	for(i=1;!feof(fp);i++)
	{
		code=(char *)realloc(code,(i+1)*sizeof(char)); //增加空间
		fscanf(fp,"%c",&code[i]);     //从文件读入下一个字符 
	}
	code[i-1]='\0';
	/到此codefile.txt文件中的字符已全部读入,存放在code数组中
    text=(char *)malloc(100*sizeof(char));
	p=text;
    m=2*n-1;
	if(*code=='0')
		find(HT,code,text,HT[m].lchild,m);   //从根节点的左子树去找
	else
		find(HT,code,text,HT[m].rchild,m);   //从根节点的右子树去找
	
    for(i=0;p[i]!='\0';i++) //把译码好的字符存入文件textfile.txt中
		fputc(p[i],fw);
	
	fclose(fp);
	fclose(fw);
	
	printf("\n对codefile.txt文件译码成功,结果已存入textfile.txt文件。\n\n");
}

//
/*将文件codefi1e以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件codeprint中。*/
void Print_code()
{
	FILE *fp,*fw;
	char temp;
	int i;
    if((fp=fopen("codefile.txt","rb"))==NULL)
		printf("Open file codefile.txt error!\n");
	if((fw=fopen("codeprint.txt","wb+"))==NULL)
		printf("Open file codeprint.txt error!\n");
	printf("\n文件codefi1e以紧凑格式显示如下:\n");
	fscanf(fp,"%c",&temp);        //从文件读入一个字符
	for (i=1;!feof(fp);i++)
	{  
		printf("%c",temp);
		if(i%50==0) printf("\n");
		fputc(temp,fw);   //将该字符存入文件codeprint.txt中
		fscanf(fp,"%c",&temp);        //从文件读入一个字符
	}
	printf("\n\n此字符形式的编码已写入文件codeprint.txt中.\n\n");
	fclose(fp);
	fclose(fw);	
}

//
/*将已在内存中的哈夫曼树以凹凸表形式显示在屏幕上,同时将此字符形式的哈夫曼树写入文件treeprint中。*/
void Print_tree()
{
	unsigned char T[100][100];
	int i,j,m=0;
	FILE *fp;
	if(n==0)
		n=Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
	
	Convert_tree(T,0,&m,2*n-1); //将内存中的赫夫曼树转换成凹凸表形式的树,存于数组T中
	
	if((fp=fopen("treeprint.txt","wb+"))==NULL)
		printf("Open file treeprint.txt error!\n");
    printf("\n以凹凸表形式打印已建好的赫夫曼树:\n");
	for(i=1;i<=2*n-1;i++)
	{
		for (j=0;T[i][j]!=0;j++)
		{
			if(T[i][j]==' ') {printf(" ");fputc(T[i][j],fp);}
			else
			{printf("%d",T[i][j]);fprintf(fp,"%d\n",T[i][j]);}
		}
		printf("\n");
	}
	fclose(fp);
	printf("\n此字符形式的哈夫曼树已写入文件treeprint.txt中.\n\n");
	
}

//
/*从文件hfmtree.txt中读入赫夫曼树,返回叶子节点数*/
int Read_tree(HuffmanTree &HT) 
{
	FILE *fp;
	int i,n;
	HT=(HuffmanTree)malloc(sizeof(HTNode));
    if((fp=fopen("hfmtree.txt","r"))==NULL)
		printf("Open file hfmtree.txt error!\n");
	for (i=1;!feof(fp);i++)
	{
		HT=(HuffmanTree)realloc(HT,(i+1)*sizeof(HTNode)); //增加空间
		fread(&HT[i],sizeof(HTNode),1,fp); //读入一个节点信息
	}
	fclose(fp);
	n=(i-1)/2;
	return n;
}


/*译码时根据01字符串寻找相应叶子节点的递归算法*/
void find(HuffmanTree &HT,char *code,char *text,int i,int m)
{
	
	if(*code!='\0') //若译码未结束
	{
		code++;
		if(HT[i].lchild==0&&HT[i].rchild==0)   //若找到叶子节点
		{
			
			*text=HT[i].ch; //将叶子节点的字符存入text中
			
			text++;
			if((*code=='0'))
				find(HT,code,text,HT[m].lchild,m); //继续从根节点的左子树找
			else
				find(HT,code,text,HT[m].rchild,m); //继续从根节点的右子树找
		}
		else   //如果不是叶子节点
			if(*code=='0')
				find(HT,code,text,HT[i].lchild,m);   //从该节点的左子树去找
			else
				find(HT,code,text,HT[i].rchild,m);   //从该节点的右子树去找
			
			
	}
	else
		*text='\0'; //译码结束
	
}


/*将内存中的赫夫曼树转换成凹凸表形式的赫夫曼树*/
void Convert_tree(unsigned char T[100][100],int s,int *i,int j)
{
	int k,l;
	l=++(*i);
	for(k=0;k<s;k++)
		T[l][k]=' ';
	T[l][k]=HT[j].weight;
	if(HT[j].lchild)
		Convert_tree(T,s+1,i,HT[j].lchild);
	if(HT[j].rchild)
		Convert_tree(T,s+1,i,HT[j].rchild);
	T[l][++k]='\0';
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值