在上一篇博客中我们介绍了二叉树的前中后序递归遍历,本篇博客我们来看看如何将递归遍历变为非递归遍历。博客链接:二叉树存储、递归遍历(前、中、后序).
前序非递归遍历
对于前序遍历我们知道它的访问顺序是根节点---->根节点的左子树---->根节点的右子树。
对于递归改为非递归,一般有两种思路:
- 借助栈实现
- 通过for循环实现
因此,我们的递归遍历就采用借助栈来实现。并且对于前序非递归遍历我们有两种思路。
思路一:
我们先思考一下,前序遍历是先访问根节点,然后依次访问根节点的左右子树。对于栈,栈的特点是先进后出,我们如何将两者结合起来呢?
先进后出?对于后出(最后访问的)节点我们首先需要把他压入栈中,所以,
-
对于
根节点
,它是一开始就被访问,直接入栈,每次直接访问
(打印、存储等操作); -
在前序遍历中,
右子树
是最后访问的,那么就需要先于左子树入栈
; -
对于
左子树
,他是第二顺序被访问,那么它就需要在右子树的后面进栈
。
我们总结一下,先序非递归的遍历实现步骤如下:
空树
—>直接返回树不为空
:将根节点入栈,循环进行以下操作- 取栈顶元素:cur = stack.peek()
- 遍历该元素:也就是访问该根节点
- 删除栈顶元素
- 如果cur的右子树存在,将右子树入栈
- 如果cur的左子树存在,将左子树入栈
代码实现:
//前序非递归遍历
public void preOrderNor()
{
if(root==null)
{
return ;
}
Stack<BTNode> s=new Stack<>();
s.push(root);
while(!s.isEmpty())
{
BTNode cur=s.peek();
System.out.print(cur.val+" ");
s.pop();
if(cur.right!=null)
{
s.push(cur.right);
}
if(cur.left!=null)
{
s.push(cur.left);
}
}
}
思路二:
以没有被遍历的节点为一颗新的子树,顺着一条路径往左走,如果当前节点有右孩子将其右孩子保存在栈中,没有则向左走(遍历该节点)
先序非递归的遍历实现步骤如下:
空树
—>直接返回树不为空
:将根节点入栈,循环进行以下操作(循环条件:栈不为空):- 取栈顶元素: cur = stack.peek()
- 让该元素出栈,循环进行以下操作(循环条件:cur!=null,即:让当前路径一直向左走):
- 遍历当前元素(cur)
- 如果cur的右子树存在,将右子树入栈
- 如果cur的右子树不存在,一直向左走( cur=cur.left )
代码实现:
public void preOrderNor2()
{
if(root==null)
{
return ;
}
Stack<BTNode> s=new Stack<>();
s.push(root);
while(!s.isEmpty())
{
BTNode cur=s.peek();
s.pop();
//cur!=null,也就是顺着一条路径一直向左
while(null!=cur)
{
//顺着cur左侧这条路路径,遍历左子树,保存右子树
System.out.print(cur.val+" ");
if(cur.right!=null)
{
s.push(cur.right);
}
cur=cur.left;
}
}
}
中序非递归遍历
中序遍历非递归的两种方式见我的另一篇博客,里边有详细的分析以及代码。
博客链接:Leetcode94-二叉树中序非递归遍历.
后序非递归遍历
在看了二叉树的中序非递归遍历后,其实后序非递归遍历和中序非递归遍历的唯一区别就是访问(打印)根节点的先后顺序不同。
具体步骤如下:
-
先找以root为根的左子树最左下的节点,并保存所经路径的所有节点-----》保存到栈里。
- 以上图中的二叉树为例,最终会停留在3这个节点。 可以认为3的左子树已经遍历结束,接下来只能遍历3的右子树
- 循环条件:while(cur!=nll || !s.empty() ),
-
获取栈顶元素
----->获取3的右子树的根节点,拿到栈顶的元素(也就是3),然后遍历3的右子树- 右子树为null:遍历3这个节点,并且将该节点出栈
- 右子树非空:将3的右子树当成新的树进行遍历,这个时候不能打印栈顶元素(值为3)否则就变成中序非递归
-
最后才打印栈顶元素(没有右孩子)
代码实现:
public void lastOrderNor()
{
System.out.println("非递归后续遍历:");
if(root==null)
{
return;
}
//1>先找以root为根的左子树最左下的节点,并保存所经路径的所有节点---》保存到栈里。
Stack<BTNode> s =new Stack<>();
BTNode cur= root ;
BTNode prev=null;//标记刚刚遍历过的节点
while (!s.empty() || cur!=null)
{
//先将左子树依次入栈
while (cur!=null)
{
s.push(cur);
cur=cur.left;
}
//2.获取栈顶元素
BTNode node=s.peek();
//node右孩子为空----》就可以遍历根节点
//node的右孩子已经遍历完成-----》就可以遍历根节点
if(node.right==null && node.right==prev)//没右孩子
{
//没有右孩子:打印该节点,并出栈
System.out.print(node.val+" ");
prev=node;//将访问过的右节点标记起来,来避免循环压栈
s.pop();
}else {//有右孩子
cur=node.right;
}
}
}