任务描述
本关任务:编写能对给定n个叶子结点,构建哈夫曼树,给出每个叶子结点对应编码的程序。
相关知识
哈夫曼编码和译码的基本原理
首先要构造一棵哈夫曼树。哈夫曼树的结点结构包括权值,双亲,左右孩子;假如由n个字符来构造一棵哈夫曼树,则共有结点2n-1个;在构造前,先初始化,初始化操作是把双亲,左右孩子的下标值都赋为0;然后依次输入每个结点的权值。
第二步是通过n-1次循环,每次先找输入的权值中最小的两个结点,把这两个结点的权值相加赋给一个新结点,并且这个新结点的左孩子是权值最小的结点,右孩子是权值第二小的结点;鉴于上述找到的结点都是双亲为0的结点,为了下次能正确寻找到剩下结点中权值最小的两个结点,每次循环要把找的权值最小的两个结点的双亲赋值不为0(i)。就这样通过n-1循环下、操作,创建了一棵哈夫曼树,其中,前n个结点是叶子(输入的字符结点)后n-1个是度为2的结点。
编码的思想是逆序编码,从叶子结点出发,向上回溯,如果该结点是回溯到上一个结点的左孩子,则在记录编码的数组里存“0”,否则存“1”,注意是倒着存;直到遇到根结点(结点双亲为0),每一次循环编码到根结点,把编码存在编码表中,然后开始编码下一个字符(叶子)。
译码的思想是循环读入一串哈夫曼序列,读到“0”从根结点的左孩子继续读,读到“1”从右孩子继续,如果读到一个结点的左孩子和右孩子是否都为0,如果是说明已经读到了一个叶子(字符),翻译一个字符成功,把该叶子结点代表的字符存在一个存储翻译字符的数组中,然后继续从根结点开始读,直到读完这串哈夫曼序列,遇到结束符便退出翻译循环。(译码内容为选做内容)
任务描述
在右侧编辑器中补充代码,编写能对给定n个叶子结点,构建哈夫曼树,给出每个叶子结点对应编码的程序。
测试说明
平台会对你编写的代码进行测试:
测试输入:5 2 7 4 5 19
预期输出:(对哈夫曼树按中序遍历输出对应叶子的哈夫曼编码)
7 00
5 010
2 0110
4 0111
19 1
测试输入数据说明:5代表共5个结点,后面的5个数字代表权值,不一定按顺序输入
开始你的任务吧,祝你成功!
/*请在此处编写代码,完成哈夫曼编码,并按中序遍历顺序输出每个叶子结点的编码*/
/********** Begin *********/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//编码结构体
struct code{
int w;//权值
int cnum;//编码个数
char c[10];//编码的倒序
int flag;//记录有没有输出
};
//哈夫曼树结点结构体
struct huffTree{
int weight;
int parent;
int lchild,rchild;
};
typedef struct huffTree* PhuffTree;
typedef struct code* Pcode;
PhuffTree creatHuffTree();
void print_huff(PhuffTree t);
int compare(char a[],char b[],int x1,int x2);
Pcode huff_code (PhuffTree t);
void print(Pcode cd);
int n;//叶子节点个数,全局变量
//创建哈夫曼树
PhuffTree creatHuffTree()
{
int i,j,min1,min2,x1,x2;//min1最小,min2第二小,x1最小的下标
scanf("%d",&n);
PhuffTree t = (PhuffTree)malloc((2*n-1)*sizeof(struct huffTree));//创建结点空间
//输入权值并初始化
for(i=0; i<n; i++)
{
scanf("%d",&t[i].weight);
t[i].lchild = -1;
t[i].rchild = -1;
t[i].parent = -1;
}
//查找两个最小的
for(i=n; i<2*n-1; i++)
{
x1 = 0;
x2 = 0;
min1 = 65535;
min2 = 65535;
for(j=0; j<i; j++)
{
if(t[j].parent == -1)
{//该节点是叶子节点
if(t[j].weight < min1)
{//比最小的还要小
min2 = min1;//原来的最小变成了第二小
x2 = x1;
min1 = t[j].weight;//变化之后的最小就是它了
x1 = j;
}
else if(t[j].weight <min2 && t[j].weight > min1)
{//比最小的大,比第二小的小
min2 = t[j].weight;//变化之后的第二小就是它了
x2 = j;
}
}
}
//更新找出来的两个结点的父节点的值
t[x1].parent = i;
t[x2].parent = i;
//给新建立的结点赋值
t[i].lchild = x1;//左节点小
t[i].rchild = x2;//右结点大
t[i].weight = min1 + min2;//权值等于二者相加
t[i].parent = -1;//没有父节点
}
return t;
}
//输出查看哈夫曼树(用来测试)
void print_huff(PhuffTree t)
{
for(int i=0; i<2*n-1; i++)
printf("第%d个的权值:%d\t,父节点:%d\t,左节点:%d\t,右节点:%d\t\n",i,t[i].weight,t[i].parent,t[i].lchild,t[i].rchild);
}
//哈夫曼树编码
Pcode huff_code (PhuffTree t)
{
Pcode cd = (Pcode)malloc(n*sizeof(struct code));
int parent;//父节点
int child;//子节点
for(int i=0; i<n; i++)//只有n位数需要编码
{
cd[i].flag = 0;//为后面的比较和输出做准备
cd[i].cnum = 0;//开始编码之前位数清零
child = i;
parent = t[i].parent;
while(parent != -1)
{//还没到根节点时,循环继续
if(t[parent].lchild == child)
{//等于左孩子时,编码0
cd[i].c[cd[i].cnum] = '0';//编码
}
else//等于右孩子时编码1
{
cd[i].c[cd[i].cnum] = '1';//编码
}
cd[i].cnum++;//编码数加一
cd[i].w = t[i].weight;//记录权值
child = parent;//以当前父节点为子节点
parent = t[child].parent; //继续找子节点的父节点
}
}
return cd;
}
//编码值比较
int compare(char a[],char b[],int x1,int x2)
{//a比b小输出1,大输出0
int i,j;
for(i=x1-1,j=x2-1; i>=0 && j>=0; i--,j--)
{//从后往前比,有其中一个等于-1时循环结束
if(a[i] < b[j]) return 1;
else if(a[i] > b[j]) return 0;
else continue;
}
if(i == -1) return 1;
else return 0;
}
//输出结果
void print(Pcode cd)
{
for(int k=0; k<n; k++)
{//一共有n个编码数需要输出
struct code tc;//用来记录一轮比较中最小的编码值
int x;//记录该编码值的下标
strcpy(tc.c,"111111");
tc.cnum = 6;
for(int i=0; i<n; i++)
{
if(cd[i].flag == 0)//未被标记,即未输出
{
if(compare(cd[i].c,tc.c,cd[i].cnum,tc.cnum) == 1)
{//前比后小
tc = cd[i];
x = i;
}
}
}
cd[x].flag=1;
//输出的部分
printf("%d ",tc.w);
for(int j=tc.cnum-1; j>=0; j--)
printf("%c",tc.c[j]);
printf("\n");
}
}
//输出编码(用来测试)
void print_code(Pcode tc)
{
for(int i=0;i<n; i++)
{
printf("%d ",tc[i].w);
for(int j=tc[i].cnum-1; j>=0; j--)
printf("%c",tc[i].c[j]);
printf("\n");
}
}
//主函数
int main()
{
PhuffTree t = creatHuffTree();
// print_huff(t);
Pcode c = huff_code(t);
// print_code(c);
print(c);
return 0;
}
/********** End *********/