C语言建立哈夫曼树编码译码


一、问题描述

1. 初始化:从配置文件Conf中读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树。
2. 编码:利用建好的哈夫曼树,对从文件ToBeTran中读入的正文进行编码,然后将结果存入文件CodeFile中。
3. 译码:利用建好的哈夫曼,从CodeFile中读取编码数据并进行译码,结果存入文件TextFile中。
4. 在终端上以直观的方式显示构造出来的哈夫曼树。 【测试数据】 基于下表给出字符集及频度,实现以下报文的编码和译码:THIS PROGRAM IS MY FAVORITE”。

二、代码实现

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//输入字符频度的最大字符数
#define MaxSize 51
//待编码语句的最大长度 
#define MaxWordSize 201

typedef struct{
	unsigned int weight;
	unsigned int lchild, rchild, parent;
}Hnode, *Htree;

void Create_Htree(Htree &HT, int &n, char ch[]);
void Encode_Htree(Htree HT, char** &HC, int n, char ch[]);
int Decode(char** HC, Htree HT, char ch[], int n);
int get_conf(char ch[], int frequency[]);
void Display_Htree(Htree HT, int end);
void find_min_node(Htree HT,int &min1,int &min2,int end);
void Encode(char** HC, char ch[], int n);

int main()
{
    int operate_num, n;
    Htree HT;
    char ch[MaxSize];
    char **HC;
    int flag = 0;
    printf("[1]创建哈夫曼树 [2]编码 [3]译码\n[4]显示哈夫曼树 [0]退出\n请输入操作:");
    scanf("%d", &operate_num);
    while(operate_num)
    {
        if(operate_num == 1)
        {
        	Create_Htree(HT, n, ch);
        	Encode_Htree(HT, HC, n, ch);
        	flag = 1;
		}
        else if(operate_num == 2)
        {
        	if(flag == 1)
        	{
        		//Encode_Htree(HT, HC, n, ch);
        		Encode(HC, ch, n);
        		flag = 1;
			}
        	else
        		printf("请先建立哈夫曼树!\n");
		}
        else if(operate_num == 3)
        {
        	int code_not_exist = 0;
        	if(flag == 1)
        	{
        		code_not_exist = Decode(HC, HT, ch, n);
        			if(code_not_exist)
        				printf("缺少待译码文件请先编码!\n");
			}
        	else
        		printf("请先建立哈夫曼树!\n");
		}
        else if(operate_num == 4)
        	if(flag == 1)
            	Display_Htree(HT, 2 * n - 1);
            else
            	printf("请先建立哈夫曼树!\n");
        else if(operate_num == 0)
        	break;
        
        else
        	printf("输入有误请重新输入\n");
        printf("[1]创建哈夫曼树 [2]编码 [3]译码\n[4]显示哈夫曼树 [0]退出\n请输入操作:");
    	scanf("%d", &operate_num);
    }
    printf("已退出"); 

	system("pause");
    return 0;
}

//创建哈夫曼树 
void Create_Htree(Htree &HT, int &n, char ch[])
{
	int frequency[MaxSize];
	int m;
	//读入字符权重的个数
	n = get_conf(ch, frequency);
	if(!n)
		return;
	m = 2 * n - 1;
	//第0个结点不存内容 
	HT = (Htree)malloc((m + 1) * sizeof(Hnode));
	Htree ptr;
	ptr = HT;
	int i;
	//初始化n个叶子结点
	for(i = 1; i <= n; i++)
	{
		(ptr + i)->weight = frequency[i - 1];
		(ptr + i)->parent = 0;
		(ptr + i)->lchild = 0;
		(ptr + i)->rchild = 0;
	}
	//初始化除叶子结点外的结点
	for(i = n + 1; i <= m; i++)
	{
		(ptr + i)->weight = 0;
		(ptr + i)->parent = 0;
		(ptr + i)->lchild = 0;
		(ptr + i)->rchild = 0;
	}
	for(i = n + 1; i <= m; i++)
	{
		//找到最小的两个叶子结点,并且min1要小于min2 
		int min1, min2;
		find_min_node(HT, min1, min2, i - 1);
		HT[i].weight = HT[min1].weight + HT[min2].weight;
		HT[min1].parent = HT[min2].parent = i;
		HT[i].lchild = min1;
		HT[i].rchild = min2;
	}
	printf("哈夫曼树已经创建\n");
}

//从文件中读取字符频度 
int get_conf(char ch[], int frequency[])
{
	FILE *fp = fopen("Conf.txt", "r");
	int i;
	fgets(ch, MaxSize, fp);
	for(i = 0; fscanf(fp, "%d", &frequency[i]) != EOF; i++);

	fclose(fp);
	return i;
}

//创建哈夫曼编码表 
void Encode_Htree(Htree HT, char** &HC, int n, char ch[])
{
	HC = (char**)malloc(n * sizeof(char*));
	char *temp = (char*)malloc(n * sizeof(char));
	temp[n - 1] = '\0';
	int i, j, j_parent, start;
	for(i = 1; i <= n; i++)
	{
		//从第一个结点开始向上求哈夫曼编码 
		j = i;
		j_parent = HT[j].parent;
		//因为是从叶子结点向根节点,所以要倒序的存入权重 
		start = n - 1;
		while(j_parent)
		{
			if(j == HT[j_parent].lchild)
			{
				temp[--start] = '0';
			}
			else if(j == HT[j_parent].rchild)
			{
				temp[--start] = '1';
			}
			j = j_parent;
			j_parent = HT[j].parent;
		}
		HC[i - 1] = (char*)malloc((n - start) * sizeof(char));
		strcpy(HC[i - 1], &temp[start]);
	}
	
	temp = NULL;
	free(temp);
}

//对文本进行哈夫曼编码 
void Encode(char** HC, char ch[], int n)
{
	FILE *fp1 = fopen("ToBeTran.txt", "r");
	char word[MaxWordSize];
	fgets(word, MaxWordSize, fp1);
	fclose(fp1);
	int length = strlen(word);
	//给数组初始化初值,防止拼接的时候出现错误 
	char encoder[MaxWordSize * 4] = {0};
	int i;
	for(i = 0; i <= length - 1; i++)
	{
		int j;
		for(j = 0; j <= n - 1; j++)
		{
			if(word[i] == ch[j])
				strcat(encoder, HC[j]);
		}
	}
	
	FILE *fp2 = fopen("CodeFile.txt", "w");
	fputs(encoder, fp2);
	fclose(fp2);
	printf("编码为:%s\n", encoder);
}

//用哈夫曼树译码 
int Decode(char** HC, Htree HT, char ch[], int n)
{
	FILE *fp1 = fopen("CodeFile.txt", "r");
	if(!fp1)
		return 1;
	char code[MaxWordSize * 4] = {0};
	fscanf(fp1, "%s", code);
	int i, j = 2 * n - 1;
	int k = 0;
	int length = strlen(code);
	char decoder[MaxWordSize] = {0};
	for(i = 0; i <= length - 1; i++)
	{
		//从根结点开始向下,0则进左孩子,1则进右孩子
		//一直到叶子结点输出 
		if(code[i] == '0')
			j = HT[j].lchild;
		else if(code[i] == '1')
			j = HT[j].rchild;
		if(HT[j].lchild == 0 && HT[j].rchild == 0)
		{
			decoder[k] = ch[j - 1];
			k++;
			j = 2 * n - 1;
		}
	}
	printf("译码为:%s\n", decoder);
	FILE *fp2 = fopen("TextFile.txt", "w");
	
	fclose(fp1);
	fclose(fp2);
	return 0;
}

//在终端上显示创建的哈夫曼树 
void Display_Htree(Htree HT, int end)
{
	int i;
	printf("序号\t权值\t左孩子\t右孩子\t双亲\n");
	for(i = 1; i <= end; i++)
	{
		printf("%d\t%d\t%d\t%d\t%d\n", i, HT[i].weight, HT[i].lchild, HT[i].rchild, HT[i].parent);
	}
}

//找到树中权值最小的两个结点 
void find_min_node(Htree HT,int &min1,int &min2,int end)
{
	int i;
	//先找min1 
	for(i = 1; i <= end; i++)
	{
		if(!HT[i].parent)
		{
			min1 = i;
			break;
		}
	}
	for(; i <= end; i++)
	{
		if(!HT[i].parent && HT[min1].weight > HT[i].weight)
			min1 = i;
	}
	//再找min2 
	for(i = 1; i <= end; i++)
	{
		//并且要保证不能和min1重复 
		if(!HT[i].parent && i != min1)
		{
			min2 = i;
			break;
		}
	}
	for(i = 1; i <= end; i++)
	{
		if(!HT[i].parent && HT[min2].weight > HT[i].weight && i != min1)
			min2 = i;
	}
}
posted @ 2022-07-31 13:11  chris599  阅读(219)  评论(0编辑  收藏  举报