使用递归遍历二叉树,已经不能熟的不能再熟了,同时还有不少问题可以用到递归的方法去求解。本文的重点不是递归遍历二叉树。
迭代的形式:
1.前序遍历
递归的方法是jvm为我们的方法调用隐式的准备了一个FILO的栈,迭代的形式就是手动维护一个栈。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if (root == null) return ans;
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
ans.add(cur.val);
//粗略的理解,因为前序遍历的顺序是——中->左->右
//而栈中的数据是先进后处,所以要想先打印左子节点
//需要先将右子节点放入栈中
if (cur.right != null) stack.push(cur.right);
if (cur.left != null) stack.push(cur.left);
}
return ans;
}
}
2.中序遍历
中序遍历,要先找到最最左边的叶子节点,弹出时又要分叉向该叶子节点的右子节点遍历,所以形式如下
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if (root == null) return ans;
Deque<TreeNode> stack = new ArrayDeque();
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
ans.add(root.val);
root = root.right;
}
return ans;
}
}
3.后续遍历
后续遍历应该是迭代遍历中最难理解的了
观察一下前序遍历的顺序:中->左->右,同时后续遍历的顺序:左->右->中,
如果我们遍历二叉树的时候先采用 中->右->左 的顺序,然后再反转一下顺序,就可以得到 左->右->中 的顺序。
PS:此题目中是采用一个List来添加节点,如果只是单单打印的话利用这种方法我还真不知道怎么写,需要另外
再看一下其他代码。
需要注意的是代码中并没有使用Colletions.reverse()方法反转List,而是采用双向链表的ans.addFirst()方法在过程中实现反转。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> ans = new LinkedList<>();
if (root == null) return ans;
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode tmp = stack.pop();
ans.addFirst(tmp.val);
if (tmp.left != null) stack.push(tmp.left);
if (tmp.right != null) stack.push(tmp.right);
}
return ans;
}
}
重头戏来了,莫里斯(Morris)方法
1.Morris中序遍历
讲一下先学习的Morris中序遍历
改动原树的情况
前面的递归和迭代,本质都是用到了栈这个数据结构,因此时间复杂度和空间复杂度都会是O(N),空间复杂度是不可避免的,而Morris遍历直接在二叉树上面改动,让二叉树在过程中变成了一个链表。其实Morris遍历和线索化链表有点像。目前做过的有剑指 Offer 36. 二叉搜索树与双向链表
例如这个二叉树:
如图所示,当前指向4这个节点,若它的左子节点不为空,则即为left,若为空则处理该节点;若有left,则一直找到left的最右最右子节点,若能找到则记为pre,再让pre.right = cur,在cur = cur.left时,消除cur 与 left 节点的连接,这样处理之后,就慢慢向链表变化了。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
TreeNode pre = null;
while(root!=null) {
//如果左节点不为空,就将当前节点连带右子树全部挂到
//左节点的最右子树下面
if(root.left!=null) {
pre = root.left;
while(pre.right!=null) {
pre = pre.right;
}
pre.right = root;
//将root指向root的left
TreeNode tmp = root;
root = root.left;
tmp.left = null;
//左子树为空,则打印这个节点,并向右边遍历
} else {
res.add(root.val);
root = root.right;
}
}
return res;
}
}