栈方式实现二叉树的遍历

递归方式就是函数自身调用自身,当递归每次调用自身时,可以看作是入栈的过程,当递归条件满足后,结束时递归再一级一级的返回,返回过程可以看作是出栈的过程。递归和栈的实现过程可以看出都是符合“先进后出,后入先出”的原则,所以递归方式其实可以转化为栈的方式来实现。

对于二叉树的遍历,先序、中序、后序遍历都可以用到递归方法实现,既然递归可以转化为栈,那么如何把对二叉树的遍历也改为用栈的思想来实现?

对于下面这个例子,我们用栈思想做中序遍历时,过程是这样的:一开始进入,遇到A结点,因为中序遍历的顺序,要把A结点的左子树都遍历完后,才把A输出。所以遇到A后,就把A结点入栈,接下来往左走,遇到子树B,同样看B的左子树,B结点的左子树存在,所以还不可以输出B,就把B入栈。再往左边走,遇到DD也先放入栈里面,接着看D的左子树,不存在,这时候就要返回了。把D输出,也就是出栈操作,把D Pop出来,最后遍历D的右子树,也没有。所以对于D来说,(遍历左子树)左子树没有—>输出D>(判断右子树)右子树没有,这时对于D结点的中序遍历就完成了,然后回到B结点,因为B结点的左子树D遍历完了,接着就输出B,也就是B出栈。B出栈后就往B的右子树走,遍历B的右子树,遇到F,把F入栈,F入栈后就遍历F的左子树,E入栈,E没有左子树了,把E出栈,E出栈后遍历E的右子树,E没有右子树,这时对E的遍历就完了,回到F结点,F结点左子树遍历完后就出栈,F出栈后,F没有右子树,这时再返回,对B的遍历的完成了,也就是对结点A的左子树遍历完成了。最后A出栈。

所以,A的左子树遍历顺序时DBEFA。右子树遍历顺序一样一开始C入栈,接着遍历左子树G,也先把G入栈,G左子树没有,G出栈,接着遍历G的右子树,HH入栈,H没有左子树,H出栈,H没有右子树,返回到GG的遍历完成了,GC的左子树,G完成遍历后,C出栈。然后对C的右子树进行遍历,I入栈,I没有左子树,I出栈,I没有右子树,返回,到了这一步,对A结点的右子树遍历也就完成了,右子树的遍历顺序是GHCI,结合起来整个二叉树的遍历顺序就是DBEFAGHCI,这个递归的中序遍历顺序是一样的。

总结一下栈方式的中序遍历过程:

1、遇到一个结点,先把它入栈,然后遍历它的左子树。

2、左子树遍历完后,把该结点出栈。

3、然后同样方法遍历右子树。

程序代码可以这样设计:

传入二叉树BT,用一个临时变量T来保存BT,然后初始化创建一个栈S,接着第89行判断当二叉树不空和或栈不空的情况下,(第90行)第一步首先判断树空不空,树不空的话就把树的根节点入栈,接着T=T>Left,也就是开始遍历左子树。也就是说第90行那个循环会一直遍历结点的左子树,直到左子树为空了。就退出了循环,接着进入第95行的循环,循环条件判断当栈不空时,也就是当左子树为空,堆栈不空的情况下,去把栈顶的元素Pop出来,再把T=T>Right,也就是中序遍历中的遍历右子树,做完第95的循环后,因为堆栈不空,还会回到第89行的循环,继续进入循环第90行遍历左子树。

上面拿了中序遍历的例子,同样先序遍历也可以从递归改为栈的方式实现。对于中序遍历,我们遇到一个结点时是先把它入栈,遍历完左子树后再print结点,对于先序,只要我们遇到一个结点,入栈后立刻把它出栈,再去遍历左子树就可以了,所以代码只需改一下顺序即可:

猜你喜欢

转载自blog.csdn.net/justinzengtm/article/details/80056106