文章目录
- 一. 题目描述
- 二. 代码 & 思路
- 1. 递归的写法
- 2. 迭代的写法(本文重点来了)
- 1) 前序
- 2) 中序
- 3) 后序
直接来个整合吧,也方便看。之前只写了递归的,现在补上迭代的(迭代才是考点!)
是面试高频题,要好好掌握好各自间的区分哦!
一. 题目描述
题目描述基本上一样,就只展示中序的题干了
二. 代码 & 思路
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,下一次循环继续peeknow = null;}// 可以继续走右边的情况else{now = now.right;}}}return ans;}
}