实验题目
实验题目: 二叉树后序遍历的非递归算法。
实验说明:二叉树后序遍历的非递归算法:结点要入两次栈,出两次栈;为了区别同一个结点的两次出栈,设置标志flag,当结点进、出栈时,其标志flag也同时进、出栈。
设根指针为root,则可能有以下两种情况:
⑴ 若root!=NULL,则root及标志flag(置为1)入栈,遍历其左子树;
⑵ 若root=NULL,此时若栈空,则整个遍历结束;若栈不空,则表明栈顶结点的左子树或右子树已遍历完毕。若栈顶结点的标志flag=1,则表明栈顶结点的左子树已遍历完毕,将flag修改为2,并遍历栈顶结点的右子树;若栈顶结点的标志flag=2,则表明栈顶结点的右子树也遍历完毕,输出栈顶结点。
二叉树后序遍历的非递归算法伪代码如下:
1. 栈s初始化;
2. 循环直到root为空且栈s为空
2.1 当root非空时循环
2.1.1将root连同标志flag=1入栈;
2.1.2 继续遍历root的左子树;
2.2 当栈s非空且栈顶元素的标志为2时,出栈并输出栈顶结点;
2.3 若栈非空,将栈顶元素的标志改为2,准备遍历栈顶结点的右子树;
实验内容
1.问题设计分析
根据实验要求,可总结如下:后序遍历的顺序是左子树、右子树、根结点,所以先将根结点及其左下结点依次进栈,即使栈顶结点p的左子树已遍历或为空,仍不能访问结点p,因为它们的右子树没有遍历,只有当这样的p结点的右子树已遍历完才能访问结点p。
难点问题:
(1)是如何判断当前处理的结点p是栈顶结点:可以设置一个布尔变量flag在do-while循环中的第一个while循环结束后开始处理栈顶结点,置flag为true;一旦转向处理右子树,置flag为false。
(2)如何判断结点p的右子树已遍历过:在一棵二叉树中,任何一棵非空子树的后序遍历序列中最后访问的一定是该子树的根结点,也就是说,若结点p的右孩子刚访问过,说明它的右子树已遍历完,可以访问结点p了。当然,若结点p的右孩子为空,也可以访问结点p。为此设置一个指针变量r,它指向刚访问过的结点,其初始值为NULL。对于正在处理的栈顶结点p,一旦p一>rchild==r成立,说明结点p的左、右子树都遍历过了,可以访问结点p。
不同于中序遍历非递归算法,这里的第二个阶段可能重复执行多次,当访问栈顶结点p之后,将其出栈,需要对新栈顶结点做同样的处理,直到p转向一棵右子树为止。此处我们用函数PostOrderl实现二叉树后序遍历的非递归算法。
2.程序编码
#include<stdio.h>
#include <malloc.h>
#define MaxSize 100
typedef char ElemType;
typedef struct node
{
ElemType data;
struct node* lchild;
struct node* rchild;
}BTNode;
void DispBTree(BTNode *b)
{
if(b!=NULL)
{
printf("%c",b->data);
if(b->lchild!=NULL||b->rchild!=NULL)
{
printf("(");
DispBTree(b->lchild);
if(b->rchild!=NULL)
printf(",");
DispBTree(b->rchild);
printf(")");
}
}
}
void PostOrderl(BTNode *b)
{
BTNode *st[MaxSize];
int top=-1;
BTNode *p,*r;
bool flag;
p=b;
do
{
while(p!=NULL)
{
top++;
st[top]=p;
p=p->lchild;
}
r=NULL;
flag=true;
while(top>-1&&flag)
{
p=st[top];
if(p->rchild==r)
{
if(p->lchild==NULL&&p->rchild==NULL)
{
printf(" %c到根结点逆路径:",p->data);
for(int i=top;i>0;i--)
printf("%c->",st[i]->data);
printf("%c\n",st[0]->data);
}
top--;
r=p;
}
else
{
p=p->rchild;
flag=false;
}
}
}while(top>-1);
}
void CreateBTree(BTNode *&b,char *str)
{
BTNode *St[MaxSize],*p;
int top=-1,k,j=0;
char ch;
b=NULL;
ch=str[j];
while(ch!='\0')
{
switch(ch)
{
case '(':top++;St[top]=p;k=1;break;
case ')':top--;break;
case ',':k=2;break;
default:p=(BTNode *)malloc(sizeof(BTNode));
p->data=ch;
p->lchild=p->rchild=NULL;
if(b==NULL)
b=p;
else
{
switch(k)
{
case 1:St[top]->lchild=p;break;
case 2:St[top]->rchild=p;break;
}
}
}
j++;
ch=str[j];
}
}
int main()
{
char str[MaxSize]="A(B(D(,G)),C(E,F))";
BTNode *b;
CreateBTree(b,str);
DispBTree(b);
printf("\n");
PostOrderl(b);
}
3.运行结果和分析
由于我们固定了输入:char str[MaxSize]="A(B(D(,G)),C(E,F))",在创建了二叉树后,后序遍历应该输出其输出结果为G-D-B-A、E-C-A和F-C-A共3条逆路径序列。结果如下:
4.实验小结
本次实验我实现了二叉树后序遍历的非递归算法。通过本次实验,我掌握了利用栈实现二叉树后序遍历的非递归算法。相比递归算法,在时间和空间上都更加高效。同时,通过编写代码,我也发现了一些自己的不足,如代码风格不够规范、变量命名不够清晰等等。这对我的日后编程也是一个很好的提醒和提升。