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;
}
}