直接来个整合吧,也方便看。之前只写了递归的,现在补上迭代的(迭代才是考点!)
是面试高频题,要好好掌握好各自间的区分哦!
一. 题目描述
题目描述基本上一样,就只展示中序的题干了
二. 代码 & 思路
1. 递归的写法
- 这里比较简单,就不赘述了,就是换换顺序。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<Integer> ans = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
mid(ans);
return ans;
}
// 前序
void first(TreeNode root){
if(root == null){
return;
}
ans.add(root.val);
first(root.left);
first(root.right);
}
// 中序
void mid(TreeNode now){
if(now == null){
return;
}
mid(now.left);
ans.add(now.val);
mid(now.right);
}
// 后序
void last(TreeNode now){
if(now == null){
return;
}
last(now.left);
last(now.right);
ans.add(now.val);
}
}
2. 迭代的写法(本文重点来了)
说实话,三份代码简直模版:while & if else & stack,写的前五行代码是一样的
- 首先明确一点,栈的作用。
- 前面的递归写法,就是 JVM 隐式地帮我们进行了栈的操作
- 迭代:用栈模仿虚拟机的调用过程
- 前序 & 中序很像,后序相对难一点,需要维护一个 pre 结点
1) 前序
class Solution {
List<Integer> ans = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
// 迭代:通过栈模拟虚拟机的递归结构
Stack<TreeNode> myStack = new Stack<>();
TreeNode now = root;
// 前中后写的前五行代码都是一样的
while(now != null || !myStack.isEmpty()){
// 非null情况:先加再说!入栈,然后直接往左走
if(now != null){
ans.add(now.val);
myStack.push(now);
now = now.left;
}
// null情况:那就走父结点的右边!
else {
now = myStack.pop().right;
}
}
return ans;
}
}
2) 中序
- 和前序相比,都是一路向左,到头了就取父结点的右边
- 不同之处在于,ans.add的位置,前序 & 中序分布在 if else 中
class Solution {
List<Integer> ans = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
// 迭代:用栈模仿虚拟机的调用过程
Stack<TreeNode> myStack = new Stack<>();
TreeNode now = root;
// 结束条件:当前结点已经走到底,并且栈中也无可用结点了
while(now != null || !myStack.isEmpty()){
// 非null情况:入栈,一路向左
if(now != null){
myStack.push(now);
now = now.left;
}
// null情况:左到头了,取出父节点,加入答案,然后从父结点的右结点继续
else{
TreeNode temp = myStack.pop();
ans.add(temp.val);
now = temp.right;
}
}
return ans;
}
}
3) 后序
- 非null情况处理,和中序完全一样
- 唯一一个使用了peek的,也就是说:null情况不一定会pop
- 代码重点在于:null 情况部分,详细见代码
- pre 的更新是从底向上的(有点抽象,结合代码可能比较好理解)
class Solution {
List<Integer> ans = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
Stack<TreeNode> myStack = new Stack<>();
TreeNode now = root;
// 相对于前序 & 中序,此处加了个 pre 结点,用来验证右边结点是否走过
TreeNode pre = null;
while(now != null || !myStack.isEmpty()){
// 非null情况:先 push 当前结点,然后往左冲!
if(now != null){
myStack.push(now);
now = now.left;
}
// null情况:冲到头了
else{
// 注意这里是 peek,不一定 pop 掉
now = myStack.peek();
// 取答案的情况:父结点无右结点 Or 父节点的右结点已走过
if(now.right == null || now.right == pre){
ans.add(now.val);
// 更新 pre 值为当前值,表示当前值已走过,供now的父节点使用
pre = now;
// pop 掉父节点
myStack.pop();
// 更新 now 为 null,下一次循环继续peek
now = null;
}
// 可以继续走右边的情况
else{
now = now.right;
}
}
}
return ans;
}
}