由于二叉树本身的递归定义性质,所以递归算法的二叉树算法容易实现,也很符合人类的思维。那么采用迭代的方式呢?这是一种模拟栈的方式,采用人工的方法来压栈出栈。严蔚敏的那本绿色书上就一个中序遍历的非递归算法。假设示例的二叉树:
中序的非递归:
递归栈分析:如上图,先一边搜索左子树一边将节点压栈,当遇到null时,则从栈中弹出一个节点,遍历这个节点,因为左子树已经访问了,故将该节点的右子树压栈即可。
示例分析:
1.将左子树一直压栈,直到压入一个null,如上图1所示。
2.pop一次,弹出null,再pop一次,访问该节点并压入该节点的右子树。如上图2。
3.重复1,2即可。
int intransfer(BTree &T ) { InitStack(S); Push(S,T); while( !StackEmpty(S) ) { while( GetTop(S,P) && P ) Push(S,p->lchild); Pop(S,P); //退null if ( !StackEmpty(S) ) { Pop(S,P); Visit(P); Push( S,p->rchild); } } } |
先序递归:
发现先序的递归栈与中序的递归栈没区别,不过访问顺序有变化,循环访问根并将根压入栈,直到压入一个空的左子树。这是pop两次,将右子树压入栈。重复迭代这个过程。
示例分析:
1.先将左子树访问并压栈,直到压入一个null。如上图1所示。
2.pop一次将null弹出,再pop一次,将该节点的右孩子入栈。如上图2所示。
3.重复1,2过程。
int preorder( BTree &T ) { InitStack(S); Push(S,T) ; while ( !StackEmpty(S) ) { while( GetTop(S,P) && p ) { Visit(P) ; Push(S,P->lchild); } Pop(S,P); //退null Pop(S,P); Push(S,P->rchild); } } |
后序遍历:由于左子树的优先权要大于右子树,故即使后序遍历,还是要先搜索左子树,遍历完左子树,然后返回看右子树是否遍历,若没有遍历,则不pop栈顶节点,待遍历完右子树就可以pop节点了。然后也是重复迭代这个过程。
示例分析:
1.将左子树压栈,直到压入一个null。
2.pop掉null,在GetTop取栈顶节点,看是否右子树访问完了,若没有则置标志位,并将右子树压栈;若访问完了,则弹出栈顶节点,并访问该节点。
int postorder( BTree &T ) { InitStack(S); Push(S,T) ; while ( !StackEmpty(S) ) { while( GetTop(S,P) && p ) { Push(S,P->lchild); } Pop(S,P) ; //退null GetTop(S,P); //取栈顶节点 if ( !P->tag ) //右子树还没访问完 { Push(S,P->rchild); P->tag = 1 ; } else { Pop(S,P); Visit(P); } } } |