已知某5个字母的哈弗曼编码,请建立这歌哈弗曼二叉树(左分支代表0,右分支代表1),并用广义表字符串的方式输出这个二叉树,在广义表中非叶子节点用“*”表示。比如字母ABCDE的哈弗曼编码为:
A:11 B:10 C:01 D:001 E:000
这颗哈弗曼二叉树的广义表输出为:*(*(* 。
【分析】:
根据字母的哈弗曼编码建立二叉树,我们可以这样思考。首先根据字母的个数,建立一个满二叉树,当然要满足要建立的二叉树是这个满二叉树的子集。这里字母是五个,我们建立的满二叉树深度是3,结点是15个,我们将每个结点都初始化为*。建完满二叉树之后我们按照哈弗曼解码的逻辑逐个扫描字母,每扫到一个字母就对应着看它的编码,逐个扫描,是‘0’就向左子树递归,‘1’就向右子树递归,当扫描到这个字母编码的最后一位是,我们就将这个结点赋值成这个字母,并将相应的左右子树设为NULL。这样我们将从这个满二叉树中建立了我们所要建立的二叉树。最后按照前序遍历的逻辑输出结果。
【算法实现】:
按照上面的那个逻辑建立二叉树。这里是数组建立的,主要是根据完全二叉树的性质,结点是i是,它的左子树就是2*i,右子树就是2*i+1。值得说明的是该问题的解决时按照堆排序、哈弗曼解码、前序遍历的思想。虽然没有完全的应用,但是懂得C语言数据结构的读者一定可以发现它们的影子。程序的数据读者可以再初始化中改。
【源代码】:
#include
#include
#include
typedef struct
{
char ch;
char str[10];
}DATA;
typedef struct btreenode
{
char ch;
struct btreenode *lchild,*rchild;
}BTREE;
void Pre(BTREE *root)
{ int tag;
if(root==NULL)
return;
if(root->ch=='*')
{printf("*(");
tag=0;
}
else
{
if(tag==0)
{printf("%c",root->ch);
tag=1;
}
else if(tag==1)
{
printf("%c",root->ch);
}
}
Pre(root->lchild);
if(root->lchild!=NULL)
printf(",");
Pre(root->rchild);
if(root->rchild!=NULL)
printf(")");
}
void main()
{
int i,j;
BTREE s[16];
BTREE *ROOT;
char str[20];
DATA data[5]={{'B',"11"},{'A',"10"},{'D',"01"},{'C',"001"},{'E',"000"}};
for(i=1;i<16;i++)
{
s[i].ch='*';
if(2*i<16)
s[i].lchild=&s[2*i];
else
s[i].lchild=NULL;
if(2*i+1<16)
s[i].rchild=&s[2*i+1];
else
s[i].rchild=NULL;
}//for
for(i=0;i<5;i++)
{
ROOT=&s[1];
for(j=0;j<4;j++)
{
if(data[i].str[j]=='0')
ROOT=ROOT->lchild;
else if(data[i].str[j]=='1')
ROOT=ROOT->rchild;
else
{
ROOT->ch=data[i].ch;
ROOT->lchild=ROOT->rchild=NULL;
break;
}
}
}//for
clrscr();
Pre(&s[1]);
getch();
}
直接使用这个说法就可以解决上面的问题了。说明一下,这里没有将这个二叉树用图形输出,需要的话就调用一下ShowTree函数就行了。
【总结】 :
建立二叉树的问题特别是完全二叉树使用数组时非常简单的,这样避免了太多次数的使用指针,因为使用指针最容易出错。其次在面对一个新的问题是我们应该从经典算法中找出能为我们所用的,当然最重要的是思想。其实数据结构学的就是思想。