前序
做法:
先让根节点入栈,然后开始while循环,将顶部元素访问并出栈,然后将它的孩子依次入栈(先右后左,因为访问顺序是先左后右),取顶,继续循环,直到栈空。
Status PreOrderTraverse( BiTree T, Status( *Visit)(Elemtype e) ) {
Mystack<BiTree> S;
BiTree p = T;
S.Push( p );
while( !S.StackEmpty() ) {
Visit( p->data );
S.Pop( p );
if( p->rchild )
S.Push( p->rchild );
if( p->lchild )
S.Push( p->lchild);
S.GetTop( p );
}
return OK;
}
中序
做法:
从根节点到左最下角的元素依次入栈,访问栈顶并出栈,右孩子入栈,循环。
Status InOrderTraverse( BiTree T, Status( *Visit)(Elemtype e) ) {
Mystack<BiTree> S;
BiTree p = T;
while( p||!S.StackEmpty() ) {
//根指针进栈,遍历左子树
if( p ) { S.Push( p ); p = p->lchild; }
else {
//根指针退栈
S.Pop( p );
if( !Visit(p->data) ) return ERROR; //访问根节点,
p = p->rchild; //遍历右子树
}
}
return OK;
}
后序——1
当前节点分3种情况:
- 是叶子节点,直接访问
- 有孩子,且都没有被访问, 则按照右孩子,左孩子的顺序依次入栈
- 有孩子,且都被访问过了, 直接访问
我们可以保存最后一个访问的节点last,如果满足 (p->right==NULL && last ==p->left) || last=p->right,那么显然p的孩子都访问过了,接下来可以访问p
Status PostOrderTraverse( BiTree T, Status( *Visit)(Elemtype e) ) {
Mystack<BiTree> S;
BiTree p = T;
BiTree last = T; //用来记住最后访问的那个节点
S.Push( p ); //根节点入栈
while( !S.StackEmpty() ) {
S.GetTop( p );
//如果是叶子节点 或 有孩子且被访问过。访问。出栈,更新最后访问节点last
if( (NULL==p->lchild&&NULL==p->rchild)||(NULL==p->rchild&&last==p->lchild)||last==p->rchild ) {
S.Pop( p ); Visit( p->data ); last = p;
}
//如果是非叶子节点,有孩子没有被访问。按照从右到左,依次入栈
else {
if( p->rchild )
S.Push( p->rchild );
if( p->lchild )
S.Push( p->lchild );
}
}
return OK;
}
后序——2
对于每个节点,都压入两遍,在循环体中,每次弹出一个节点赋给p,如果p仍然等于栈的头结点,说明p的孩子们还没有被操作过,应该把它的孩子们加入栈中,否则,访问p。也就是说,第一次弹出,将p的孩子压入栈中,第二次弹出,访问p。
Status PostOrderTraverse_2( BiTree T, Status( *Visit)(Elemtype e) ) {
Mystack<BiTree> S;
BiTree p = T,top;
S.Push( p );
S.Push( p ); //根节点入栈,入两次
while( !S.StackEmpty() ) {
S.Pop( p ); S.GetTop( top );
//如果p仍然等于栈的头结点,说明p的孩子们还没有被操作过,应该把它的孩子们加入栈中
if( !S.StackEmpty()&&top==p ) {
if( p->rchild ) { S.Push( p->rchild ); S.Push( p->rchild ); }
if( p->lchild ) { S.Push( p->lchild ); S.Push( p->lchild ); }
}
//否则,访问p。也就是说,第一次弹出,将p的孩子压入栈中,第二次弹出,访问p。
else
Visit( p->data );
}
return OK;
}