哈夫曼树的文件操作

哈夫曼树的文件操作

1. 问题描述:

利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(解码)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站设计一个哈夫曼编/译码系统。

2.一个完整的系统应具有以下功能:

1)初始化(Initialzation)。从数据文件 DataFile.data 中读入字符及每个字符的权值,建立哈夫曼树 HuffTree;

2)编码(EnCoding)。用已建好的哈夫曼树,对文件 ToBeTran.data 中的文本进行编码形成报文,将报文写在文件 Code.txt 中;

3)译码(Decoding)。利用已建好的哈夫曼树,对文件 CodeFile.data 中的代码进行解码形成原文,结果存入文件 Textfile.txt 中;

4)输出(Output): 输出ToBeTran.data 及其报文 Code.txt;输出 CodeFile.data 及其原文 Textfile.txt;

要求:所设计的系 统应能在程 序执行的过程中,根据 实际情况(不同的输入 )建立DataFile.data、ToBeTran.data 和 CodeFile.data 三个文件,以保证系统的通用性。

编程思想:

1)根据哈夫曼树的定义:有n个叶子节点的哈夫曼树必有m = 2 * n - 1个节点。
构造哈夫曼树时:将前n个节点初始化相应的字符和权值,从中找到两个父母节点为0(意味着未被访问过)且权值最小的两个数作为第n+1个节点的左右孩子,依次初始化剩余m-n个节点。

2)根据哈夫曼树的定义:有n个叶子节点的哈夫曼树其深度不会超过n-1。
编码时:1.定义临时存放哈夫曼编码的数组cd[],从叶到根逆向求编码,将得到的数据倒着存入数组cd[]中,再将数组cd拷贝到HC中,2.再找下一个叶节点,将得到的数据再倒着存入数组cd[]中,再将数组cd拷贝到HC中。重复2,最后不要忘了释放cd[]的空间。

3)译码时:依据编码向下遍历树,直到向下碰到叶子节点。

4)输出文件内容时:用一个数组保存文件名。

注意!!!

  • 打开文件时文件的路径(本程序将文件放在程序运行的同一目录下)

  • 输出文件内容时,输入的文件名要带后缀名。

  • 为便与更好的使用可利用for循环和控制输出语句将哈夫曼树和每个字母对应的哈夫曼编码输出。(本程序未写)。

运行:

  • 假设建立的哈夫曼树:
    字符分别为a s d f
    权值分别为1 2 3 4

  • 在文件DataFile.data中写下1a2s3d4f#(以#作为结束标志)
    在文件ToBeTran.data中写下asdf#(以#作为结束标志)
    在文件CodeFile.data中写下110111100#(以#作为结束标志)

  • 运行功能一
    会建立如图所示的哈夫曼树:
    哈夫曼树的文件操作_第1张图片

  • 运行功能二
    会生成相应的哈夫曼编码a–110,s–111,d–10,f–0
    会生成文件Code.txt,内容为110111100#

  • 运行功能三
    会生成文件Textfile.txt,内容为asdf#

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define N 100
#define ERROR 0
#define OK 1
typedef struct {
	int weight;
	char  ch;
	int parent,lchild,rchild;
}HTNode,*HuffmanTree;  //动态分配数组存储哈夫曼树
typedef char **HuffmanCode;  //用于存储哈夫曼编码的数组
void Select(HuffmanTree &HT,int i,int &s1,int &s2)
{//找哈夫曼树中权值最小的两个节点
	int temp,j,k;
	//找权值最小节点
	for(j = 1;j <= i;j++)//找父母为0的节点temp
		if(HT[j].parent == 0)
		{
			temp = j;
			break;
		}
	for(j = 1;j <= i;j++)
		if (HT[j].parent == 0) 
			if (HT[j].weight < HT[temp].weight) //设temp为最小节点
				temp=j;//如果比temp小,则交换
	s1 = temp;
	//找权值次小节点
	for(k = 1;k <= i;k++)//再找父母为0的节点temp
		if(HT[k].parent == 0 && k != s1)
		{
			temp = k;
			break;
		}
	for(k = 1;k <= i;k++)
		if (HT[k].parent == 0 && k != s1) //找不与s1相等的最小节点
			if (HT[k].weight < HT[temp].weight) 
				temp=k;
	s2 = temp;
}
void  Initialzation (HuffmanTree &HT,int n)
{ //n个权值,构造哈夫曼树HT
	FILE *fp;
	int m,i,s1=1,s2=1;
	if ((fp = fopen("DataFile.txt", "r")) == NULL) 
	{
		printf("文件读取失败\n");
		exit(0);
	}
	if(n < 1) 
		return ;
	m = 2 * n - 1;
	HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));
	for(i = 1;i <= m;i++) //0号不用,进行初始化
		HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
	for(i = 1;i <= n;i++) //建n棵仅含根结点的二叉树的森林
		fscanf(fp, "%d%c", &HT[i].weight,&HT[i].ch); 
	fclose(fp);
	for( i = n + 1;i <= m;i++) //建哈夫曼树
	{ //在HT[1..i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2
		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;
	}   
}
void  EnCoding (HuffmanTree HT,HuffmanCode &HC,int n)
{ //从叶子到根逆向求每个字符的哈夫曼编码
	FILE *fp, *fp1;
	char *cd;
	int i,start,c,f;
	HC=(HuffmanCode)malloc((n+1)*sizeof(char*));  //分配n个字符编码的头指针
	cd =(char*)malloc(n*sizeof(char)); //分配求编码的工作空间
	cd[n-1]='\0';   //编码的结束标志
	for(i=1;i<=n;i++) //逐个字符求哈夫曼编码
	{
		start=n-1;    //编码结束符位置
		c=i;
		f=HT[i].parent;  //从叶到根逆向求编码
		while( f!=0)
		{
			if(HT[f].lchild==c) 
				cd[--start]='0';
			else
				cd[--start]='1';
			c=f;
			f= HT[f].parent;
		}
		HC[i]=(char*) malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
		strcpy(HC[i],&cd[start]); //从cd复制编码串到HC[i]中
	} 
	free(cd); //释放工作空间
	if ((fp = fopen("ToBeTran.txt", "r")) == NULL) {
		printf("文件错误\n");
		exit(0);
	}
	if ((fp1 = fopen("Code.txt", "w")) == NULL) 
	{
		printf("文件错误\n");
		exit(0);
	}
	c = fgetc(fp);
	while(c != '#') 
	{
		for(i = 1;i <= n;i++) //建n棵仅含根结点的二叉树的森林
		{
			if(c == HT[i].ch) //找到字母的编号
			fprintf(fp1, "%s", HC[i]);//将对应的哈夫曼编码写入文件中
		}
		c = fgetc(fp);
	}
		fprintf(fp1,"#"); 
		fclose(fp);
		fclose(fp1);
}
void Decoding(HuffmanTree HT, HuffmanCode HC,int n)
{//译码
	int i;
	char b;
	FILE *fp,*fp1;
	if ((fp = fopen("CodeFile.txt", "r")) == NULL) {//打开文件只读
		printf("文件错误\n");
		exit(0);
	}
	if ((fp1 = fopen("TextFile.txt", "w")) == NULL) {//打开文件只写
		printf("文件错误\n");
		exit(0);
	}
	b = fgetc(fp); //从文件中一个一个读取字符
	i = 2 * n - 1;
	while (b != '#')
	{
		if (b == '0')//如果为'0',则指向其左孩子
			i = HT[i].lchild;
		else if (b == '1')//如果为'1',则指向其左孩子
			i = HT[i].rchild;
		if (HT[i].lchild == 0)
		{//直到碰到叶子节点
			fprintf(fp1, "%c", HT[i].ch);
			i = 2 * n - 1;
		}
		b = fgetc(fp);//再读下一个字符
	}
	fprintf(fp1,"#"); //将‘#’写入文件
	fclose(fp);//关闭文件
	fclose(fp1);
}
void Print(char *filename)
{
	FILE *fp;
	if ((fp = fopen(filename, "r")) == NULL) {
		printf("文件错误\n");
		exit(0);
	}
	char b = fgetc(fp);
	while (b != '#')
	{
		printf("%c ",b);
		b = fgetc(fp);
	}
	fclose(fp);
}
int main()
{
	int a,k,n;
	char filename[N];//用于保存文件名
	HuffmanTree HT;
	HuffmanCode HC;
	printf("输入文件字母数:\n");
	scanf("%d",&n);
	for(k = 0;;k++)
	{
        printf("\n1.从文件DataFile中读取建立哈夫曼树\n");
		printf("2.对文件ToBeTran编码再写入Code中\n");
		printf("3.对文件CodeFile译码再写入Textfile中\n");
		printf("4.输出文件内容\n");
		printf("5.退出程序\n");
		printf("输入你的选择:\n");
		scanf("%d",&a);
		switch (a)
		{
		    case 1:
				Initialzation(HT,n);	
				break;
			case 2:
				EnCoding(HT,HC,n);
				break;
			case 3:
				Decoding(HT,HC,n);
				break;
			case 4:
				printf("请输入文件名(带后缀名):\n");
				scanf("%s",filename);
				Print(filename);
				break;
			case 5:exit(0);
		}
	}
	return OK;
}

你可能感兴趣的:(数据结构)