用栈实现二叉树的前序遍历
我们大多数遍历二叉树的时候都是使用的递归的方式遍历, 但是这里我们来学习一下如何用栈实现二叉树的前序遍历, 前序遍历只是举个例子, 会了前序遍历之后中序遍历和后续遍历也就会了
- 对于我们使用栈遍历二叉树的时候前序中序和后续的难度是一致的, 但是有些算法中是后序遍历的难度要比前序遍历和中序遍历的难度大 —> morris遍历就是后序遍历的难度比前序和中序遍历难度略大
注意: 我们这里要求最终将遍历的结果装入到一个List之后返回
递归方式的二叉树前序遍历:
//这里我们同样可以选择将我们的ArrayList创建在成员位置(创建在成员位置我们就直接所有方法都在这一个成员 //位置的ArrayList中添加就可以了)
ArrayList<Integer> arrayList = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
//如果根节点不存在, 为null, 那么直接返回一个空的集合即可
if(root == null){
return new ArrayList<>();
}
arrayList.add(root.val);
List<Integer> list = preorderTraversal(root.left);
List<Integer> list1 = preorderTraversal(root.right);
arrayList.addAll(list);
arrayList.addAll(list1);
return arrayList;
}
其实递归的底层就是在调用我们的系统栈, 在Java中递归其实就是开辟了很多栈帧, 每个栈帧就对应着一个开辟的方法的作用域, 栈帧中有一个部分称之为局部变量表, 在局部变量表中第一个位置放的就是this, 所以我们的所有栈帧是共享一个成员变量的
那么这里我们就相当于模拟递归的内部执行 (用栈来完成)(用力扣144题检查是否正确):
package com.ffyc.algorithm.Solution;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/*
使用栈来实现前序遍历
1. 定义一个操作枚举类, 因为我们只有两个操作对象, 属性不用定义 ---> 这个枚举类其实也可以不用定义
2. 定义一个命令类, 命令类中有两个属性, 一个是操作, 一个是操作结点(就类似于我们的指令)
*/
public class Solution144 {
//定义枚举类
private enum Operate{
PRINT,
PUSH;
}
//定义一个命令类
private class Command{
//定义两个属性, 一个操作, 一个操作结点(操作数)
Operate operate;
TreeNode treeNode;
//提供构造方法
public Command(Operate operate, TreeNode treeNode) {
this.operate = operate;
this.treeNode = treeNode;
}
}
public List<Integer> preorderTraversal(TreeNode root) {
//判断如果头结点为空, 那么直接返回一个空的列表即可
if(root == null){
return new ArrayList<>();
}
//创建一个list存储结果
List<Integer> res = new ArrayList<>();
//创建一个栈, 实现递归
Stack<Command> stack = new Stack<>();
//定义一个头结点对应的添加操作
Command command = new Command(Operate.PUSH, root);
stack.push(command);
while(!stack.isEmpty()) {
Command pop = stack.pop();
if (pop.operate == Operate.PRINT) {
res.add(pop.treeNode.val);
} else {
if (pop.treeNode.right != null) {
stack.push(new Command(Operate.PUSH, pop.treeNode.right));
}
if (pop.treeNode.left != null) {
stack.push(new Command(Operate.PUSH, pop.treeNode.left));
}
stack.push(new Command(Operate.PRINT, pop.treeNode));
}
}
return res;
}
}
- 其实就是定义了一个操作枚举类和一个命令类, 然后定义了一个用栈来完成这些命令的存储和执行的方法, 结合到一起就可以完成我们的二叉树的前序遍历
- 如果想变成中序遍历和后序遍历只需要改不同的打印时机就可以了
这里我们给出后序遍历的代码:(力扣 145题)
package com.ffyc.algorithm.Solution;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/*
用栈来实现二叉树的后序遍历
*/
public class Solution145 {
//定义枚举类
private enum Operate{
PRINT,
PUSH;
}
//定义一个命令类
private class Command{
//定义两个属性, 一个操作, 一个操作结点(操作数)
Operate operate;
TreeNode treeNode;
//提供构造方法
public Command(Operate operate, TreeNode treeNode) {
this.operate = operate;
this.treeNode = treeNode;
}
}
public List<Integer> postorderTraversal(TreeNode root) {
//判断如果头结点为空, 那么直接返回一个空的列表即可
if(root == null){
return new ArrayList<>();
}
//创建一个list存储结果
List<Integer> res = new ArrayList<>();
//创建一个栈, 实现递归
Stack<Command> stack = new Stack<>();
//定义一个头结点对应的添加操作
Command command = new Command(Operate.PUSH, root);
stack.push(command);
while(!stack.isEmpty()) {
Command pop = stack.pop();
if (pop.operate == Operate.PRINT) {
res.add(pop.treeNode.val);
} else {
//某个结点只要有做左子节点或者右子节点, 那么肯定是最终会出栈该左子节点的push和右子节点的push操作, 只有左子节点和右子节点没有的时候才会出栈print操作
stack.push(new Command(Operate.PRINT, pop.treeNode));
if (pop.treeNode.right != null) {
stack.push(new Command(Operate.PUSH, pop.treeNode.right));
}
if (pop.treeNode.left != null) {
stack.push(new Command(Operate.PUSH, pop.treeNode.left));
}
}
}
return res;
}
}