二叉树的非递归遍历(二叉链表)

中序遍历

先序遍历

后序遍历

二叉树的先、中、后序非递归遍历

其实之前就写过一篇非递归遍历的文章,当时可以说是很顺利,今天再次写非递归遍历,本以为会非常顺利,但是实际上非常坎坷,所以决定记录一下自己的心得体会。

1、队列、栈的元素最好设置为指向结点的指针。

在树的广度优先遍历(层序遍历)中,为了记录输出结点的顺序,我们采用了队列这种数据结构。在树的深度优先遍历(先、中、后序遍历)中,为了获取结点的左右子树或输出结点值,我们利用了栈这种数据结构。

队列、栈的元素设为指向结点的指针与设为结点的区别在于,我们的算法中,有没有这样一个步骤,它是需要改变树中结点的数据值的。若有,则应该设置指针变量,否则没必要。在非递归遍历中的后序遍历中,我们需要记录每个结点进入栈的次数,当该结点已经进入过栈且出栈过一次之后,我们就不被允许再将该结点入栈了,因为入栈意味着输出,若进行了两次入栈操作,那势必进行了两次输出,显然与遍历的定义不相符。

那为什么队列也要定义为指针呢?因为指针比结点更具有普遍性,或者说更具有健壮性。在广度优先遍历中不需要更改结点的数值域,但若是换了一个问题,没准就需要更改,此时若是指针,则直接使用即可,若是结点,则会麻烦许多。再一个就是方便记忆,大家都是指针,这样也不用取分什么时候用指针,什么时候用结点。

2、每种遍历顺序最需要考虑的三个事情:什么时候入栈,什么时候出栈,什么时候输出

因为在之前的那篇文章里面讲到,遍历二叉树时需要取分两个动作,“读到”和“输出”,意思是我们读到该结点时并不一定就需要立即输出它。

在三种遍历顺序中,结点都是在“读到”后入栈的。

什么时候出栈与为什么要入栈是同一个问题,即当入栈的目的都达到时,结点即可出栈。

什么时候输出取决于遍历的顺序,下面一一阐述。

先序遍历:入栈是为了找到该结点的右孩子,那么什么时候需要用到该结点的右孩子呢?就是该结点的左子树遍历完之后会开始遍历该结点的右孩子,那么什么时候左子树遍历完呢?这要分两种情况,第一种是该结点的左子树为空,则判空之后直接遍历该结点的右孩子,另一种是该结点的左子树遍历完了,一棵树遍历完毕的特征就是这棵树中的所有结点都进行了一组入、出栈操作,这就意味着此时,栈顶元素变成了当前结点,即该结点第二次成为栈顶元素,此时便说明该读取该结点的右子树了。什么时候输出呢?不难理解,入栈之后立即输出。(在先序遍历中,读到即为输出)

中序遍历:入栈是为了当该结点第二次成为栈顶元素时输出并找到该结点的右孩子。输出完该结点就改用到该结点的有孩子了。什么时候输出呢?该结点左子树遍历完毕后就该输出该结点了。什么时候该结点左子树遍历完毕呢?因为中序遍历和先序遍历一样,都是最后遍历右子树的右结点,所以遍历完一棵树的标志相同。

后序遍历:入栈是为了输出该结点和找到该结点的右孩子(与中序遍历相同)。后序遍历一大特点是输出之后立即出栈,这与先序遍历有点相似。

3、三种遍历顺序的横向对比

相同点

先序遍历与中序遍历算法大致相同,不同的是输出结点的时机不同。先序遍历是入栈后就输出,中序遍历是左子树读取完毕后输出,具体体现在两个部分,即当前结点左孩子为空,输出,左子树遍历完毕,输出。

先序遍历与中序遍历都是两个while循环,而后序遍历只有一个while循环。

三者都是先while循环当前结点左孩子是否为空,非空入栈,若空的话,后序遍历会与先、中序遍历进行不同的操作。先、中序遍历是若右孩子非空则入栈然后当前循环结束,即进入下一个大循环,若空则出栈,直到右孩子非空或栈为空为止。后序遍历是 若右孩子非空则入栈,然后进入下一个大循环,若空则出栈并进入下一个大循环。

后序遍历需要记录某一结点是否完成过一次入、出栈操作。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗马尼亚硬拉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值