(如何构造一个广义表已经略去)
对于广义表化的树,我们采用的树节点类似二叉链表形式的存储。
typedef char ElemType; //数据项类型
typedef struct tnode //定义结点类型
{ ElemType data; //结点的值————由广义表给出
int width; //子叶宽度————设计算法求子叶宽度
int height; //结点高度————初始化时设置结点的高度
int pos; //结点位置————设计算法求结点位置
int ifbro=1; //是否有兄长————根据兄弟指针的内容判断是否有兄长结点
struct tnode *hp; //指向兄弟————初始化时进行赋值
struct tnode *vp; //指向孩子结点————初始化时进行赋值
} TSBNode; //树节点
int TWidth(TSBNode* &tree) //计算子叶宽度
{
int width=0; //初始化为0
TSBNode *treebro; //孩子们的长兄结点
if(tree->vp!=NULL) //有子女时计算子女总的宽度
{
treebro=tree->vp;
while(treebro!=NULL)
{
width+=TWidth(treebro);
//将子女数量转化为子叶宽度
treebro=treebro->hp;
//循环终止条件的一部分
}
}
else //这就到了叶子结点了
return 1;
return width; //返回该结点的子叶宽度
}
得到计算子叶宽度的算法之后,需要为每一个结点赋值。
void Twidth(TSBNode* &tree) //为结点子叶宽度赋值
{
tree->width=TWidth(tree);
if(tree->hp!=NULL)
{
Twidth(tree->hp); //将函数推向兄弟结点
}
if(tree->vp!=NULL)
{
Twidth(tree->vp); //将函数推向子女结点
}
return;
}
至此,调用Twidth函数会为输入的树每一个结点进行宽度赋值。
void LocateTreep(TSBNode* &tree,int fpos,int fwidth,int flag) //计算一个结点应放置位置
{
if(flag) //表示从父结点继承pos
tree->pos=fpos-(fwidth/2+1)+(tree->width/2+1);
else //表示从兄长结点继承pos
tree->pos=fpos+(fwidth/2)+(tree->width/2+1);
}
关于为什么函数中会出现fwidth/2+1和fwidth/2这样“奇怪的长度”,可以动手画制一棵树,观察层与层之间树结点的分布,以及层内兄弟结点的分布。
接下来为每一个结点赋上位置信息:
void LocateTree(TSBNode* &tree) //实现各个结点的定位
{
if(tree->height==1) //如果是根节点
tree->pos=tree->width/2+1;
if(tree->hp!=NULL) //将函数推向兄弟结点
{
LocateTreep(tree->hp,tree->pos,tree->width,0);
LocateTree(tree->hp);
}
if(tree->vp!=NULL) //将函数推向孩子结点
{
LocateTreep(tree->vp,tree->pos,tree->width,1);
LocateTree(tree->vp);
}
}
接下来的构造实际上融合了多个算法,只是求取这些内容的过程中很多循环过程、判断过程是相似的,故结合在一起作为一个大的构造函数。
读者可以适当将函数进行拆分,得到多个不同的函数适用于求取不同的量值。
TSBNode CreatTree(TSBNode* &tree,char *str) //构造树
{
TSBNode *St[MAXSIZE], *Fake_St_top[MAXSIZE], *p=NULL;
// 双亲结点 兄弟结点 备用指针结点
int top=-1,k,j=0; //记录层数top;分类标志k;循环变量j;
char ch; //当前广义表指示变量ch;
ch=str[j];
tree=NULL; //基底树
while (ch!='\0')
{
switch(ch)
{
case'(':top++; St[top]=p; k=1; break; //构造该树的孩子树
case')':top--; break; //向上一层回到双亲结点
case',':k=2;break; //构造该树的兄弟结点
default:p=(TSBNode *)malloc(sizeof(TSBNode)); //开始构造结点
p->data=ch; //设置值域
p->hp=NULL; //设置兄弟指针
p->vp=NULL; //设置孩子指针
p->height=top+2; //设置高度(个人习惯将根节点记为第1层,以此类推)
if (tree==NULL) //从根结点构造开始
{
tree=p;
tree->ifbro=0;
}
else //很重要的部分(建议搭配画图理解)
{
switch(k)
{
case 1: St[top]->vp=p; //将该结点与双亲结点联系
St[top]->vp->ifbro=0; //该结点作为长兄结点,没有兄长!
Fake_St_top[top]=p; break; //指针后移
case 2: Fake_St_top[top]->hp=p; //将该结点与兄长相联系
Fake_St_top[top]=p; break; //指针后移
}
}
}
j++; //继续处理广义表的下一位符号
ch=str[j]; //更新变量(即装入下一位符号)
}
//这两个函数可以在main函数里调用,为了构造树时的完整性,直接选择了在此处调用。
Twidth(tree); //定宽
LocateTree(tree); //定位
}
为什么这里并没有设return语句呢?首先这里采用了比较高级一些的TSBNode* &tree——&符号的用法直接使tree作为了返回型参数。那为什么TSBNode换成void就会失败呢?我还未做进一步的学习……(心虚,大佬们路过看到的话就帮我解释在评论去吧,小弟先谢过……
这就有一棵内容齐全、形式规整的链表形式存储的树了。
从例图,我们要知道输出合适的树,关键是:结点的位置!结点的位置需要有结点的高度和结点的位置,结点的高度已经求出来了,结点的位置我们也有了,如何输出是一门学问。
在此我采用了树的层次遍历的方法输出,但并不仅仅输出值域内容,还有穿插的制表符来划清各结点之间的位置关系。
void DispTree(TSBNode* &tree) //显示构造树
{
//**************************************入队部分******************************************
TSBNode *Qu[MAXSIZE],*treebro; //采用循环队列方式存储————为了层次遍历
//treebro并不是长兄指针————仅仅是指向兄弟指针
int front=0,rear=0,op=0; //front是队首指针;rear是队尾指针;op
if (tree!=NULL) //拒绝空树
rear++; //队尾指针移动————准备存放数据
Qu[rear]=tree; //层次遍历————第一层
while (rear!=front)
{
front++;
if (Qu[front]->vp!=NULL) //如果有孩子结点,将孩子结点一串入队
{
treebro=Qu[front]->vp; //兄弟指针就位
while(treebro!=NULL)
{
rear=(rear+1)%MAXSIZE;
Qu[rear]=treebro;
treebro=treebro->hp; //兄弟指针后移
}
}
}
rear++; //后移一位,为了构成下面的循环终止条件
//***************************************出队部分****************************************
front=1;
while (rear!=front)
{
if(Qu[front]->height>Qu[front-1]->height) //判断是否应该换层
{
printf("\n\n"); //层间距问题,个人审美偏好啦~
op=0;
}
//到达pos之前循环输出制表符以求合适比例
while(op<Qu[front]->pos)
{
printf("\t");
op++;
}
if(Qu[front]->ifbro==0) //没有兄长结点应该冠以“{”号界定层范围
printf("\b{");
printf("%c",Qu[front]->data);
if(Qu[front]->hp==NULL) //没有兄弟结点应该冠以“}”号界定层范围
printf("}");
front++; //循环终止条件
}
}
int main()
{
//****************************************接受数据
printf("请输入广义表形式表示的树:\n");
char s[MAXSIZE]="\0";
scanf("%s",s);
//****************************************完成目的
char*str=s;
TSBNode* tree;
CreatTree(tree,str); //构造树
DispTree(tree); //输出数
return 0;
}
To be continued……