(一)了解递归实现前序遍历的底层原理
对于二叉树最经典的就是前中后序遍历的递归实现。以前序遍历为例,宏观来看,只要当前节点不为空,就将当前节点存入list,遍历左子树遍历右子树。最终即可完成整棵树的遍历。
通过java代码实现一棵二叉树的遍历,代码如下:
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
Class Solution{
List<Integer> list = new ArrayList<Integer>();
public List<Integer> preorderTravel(TreeNode root){
if(root!=null){
list.add(root.val);
preorderTravel(root.left);
preorderTravel(root.rigth);
}
return list;
}
}
来分析一下,系统内部的运行,以下面这个简单的二叉树的前序为例:
那首先知道系统中的所有递归遍历都是通过系统栈来实现的,而栈这种数据结构特点是先进后出。所以我们要前序遍历当前这棵树他的顺序应该是,访问当前节点1的值,访问左子树,访问右子树。对应的入栈顺序就是,访问右子树、访问左子树、访问节点1。我们将访问指令定义为print,访问左子树指令定义为left,访问右子树指令定义为right。
那么访问1节点其栈内信息如图:
此时开始第一步访问,print出栈,没有后续操作,那么继续出栈,left1访问当前节点左子树。此时,需要将左子树的访问顺序入栈,系统栈信息如图:
此时系统无操作继续访问系统栈,取出第一条指令,print2填入list无操作。再取出来left2,此时转向节点2的左子树,而其为空直接返回当前系统栈。所以再从系统栈取出right2,同样他的左右子树都为空也直接返回。此时系统栈信息如图:
取出right1,此时其节点不为空,那么将right1对应操作存入系统栈:
再取出第一条指令,print3,添加到list后,无后续操作。再取出left3,3节点左子树为空直接返回。再取出right3,3节点右子树为空直接返回。再取系统栈指令,系统栈为空。遍历结束:
从上述过程可以看出,系统栈存储着访问指令,是输出指令还是转向左子树或者右子树进行执行指令。当输出指令,直接输出,再取指令,此时取出来的转向指令出栈后,若访问的节点不为空,则将访问节点的3条访问指令入栈即可。一直按照这种顺序去访问系统栈,直到栈中没有指令,二叉树的递归遍历也就结束了。
(二)模拟系统栈完成二叉树非递归遍历
除了教课书给出的经典的非递归方式外,我们总结一下另一种方式——模拟系统栈,来完成二叉树的非递归操作。
根据上面的步骤,我们需要建立栈(存储指令)和指令来辅助完成该程序。
public class Commond {
String s;//指定信息,print or go(go left ,go rigth)
TreeNode node;
Commond(String s, TreeNode node) {this.s= s;this.node = node;}
}
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();//存二叉树节点信息
if(root == null)
return list;
//模拟系统栈,存储指令信息
Stack<Commond> stack = new Stack<Commond>();
//设置初始值位当前节点
stack.push(new Commond("go",root));
//栈不为空的时候循环访问,取指令
while(!stack.isEmpty()){
Commond cd = stack.pop();
//如果是访问指令,直接存入集合中
if(cd.s == "print")
list.add(cd.node.val);
else{
//注意入栈的顺序奥
if(cd.node.rigth!=null)
stack.push(new Commond("go",cd.node.rigth));
if(cd.node.left!=null)
stack.push(new Commond("go",cd.node.left));
stack.push(new Commond("print",cd.node));
}
}
return list;
}
}
以上就是对leecode144号问题,二叉树前序遍历的递归和非递归实现方式。呐对于94和145两个问题。仅需要改变上面代码的顺序就OK啦。那下一篇文章将会介绍二叉树递归遍历的一些题目~要加深递归遍历的宏观理解奥
(三)教科书通过stack完成二叉树的非递归遍历
下面给出教科书中,通过stack完成二叉树的非递归前序遍历。对于中序遍历和后续遍历,相对复杂,建议可以再理解(二)的基础上,通过二来实现。
public ArrayList<Integer> preOrder(TreeNode root) {
//存储节点中的数据信息(遍历顺序)
ArrayList<Integer> list = new ArrayList<>();
//存储二叉树遍历过程中的节点
Stack<TreeNode> stack = new Stack<>();
//初始化
stack.push(root);
while(!stack.empty()){
TreeNode treeroot = stack.pop();
list.add(treeroot.val);
//根据栈的特点,先添加右子树再添加左子树
if(treeroot.right!=null){
stack.push(treeroot.right);
}
if(treeroot.left!=null){
stack.push(treeroot.left);
}
}
return list;
}