题干分析:
首先要注意题目中说到的路径,不是在visit()时得到的序列,而是看某时刻栈中的序列。这里的路径是这样理解的,访问一个结点 x 时,栈中结点恰好是 x 结点的所有祖先,从栈底到栈顶结点再加上 x 结点,这样就构成了从根结点到 x 结点的一条路径。
在王道数据结构P134中有讲到这句话。
比如先序遍历的非递归算法,在n进栈之前,m就已经出栈了。中序也是同样的。
- 下面以先序为例,对照代码,演示一下栈中元素的出入栈过程。
void PreOrder(BiTree T){InitStack(S);BiTree p = T;while(p||!IsEmpty(S)){if(p){visit(p);Push(S,p);p=p->lchild;}else{Pop(S,p); //注意Pop操作!p=p->rchild;}}
}
1、p指向m结点,进入循环,p非空,将m入栈,p=p->lchild,p又指向了a结点。见图1。
2、满足条件,再进入循环,p非空,a入栈,p=p->lchild,此时p为null。见图2。
3、此时栈不为空,进入循环,p为null,栈顶元素a出栈时,p指向a结点,p=p->rchild 后,p还是为null。见图3。
4、此时栈还是不为空,进入循环,p为null,栈顶元素m出栈时,p指向m结点(注意在Pop(S,p)操作中,栈顶元素出栈时,p 是指向出栈的那个元素的,有一个赋值的步骤),p=p->rchild,p现在指向了b结点。见图4。(图中p是m出栈时的指向,p’是p->rchild赋值后的指向)此刻栈中元素为空。在n进栈之前,m就已经出栈了。栈中已经没有从m到n的路径了。所以先序遍历不适合。
总结
中序遍历的非递归算法和先序相比只是 visit() 的位置不同,visit() 的位置不影响栈中的路径,所以先序、中序都不适合。只有后序遍历的非递归算法可以,当利用后序遍历到目标结点 n 时,栈中的序列就为 n 结点的全部祖先结点。(路径顺序是从栈底到栈顶!)
小伙伴们可以用文中此例自行验证后序非递归的过程。