二叉树的中序遍历
涉及知识:二叉树的遍历
定义结点的结构:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */
1. 递归
很经典的方法,没什么好解释的。
class Solution { private List<Integer> list = new ArrayList<>(); public List<Integer> inorderTraversal(TreeNode root) { inorderTra(root); return list; } public void inorderTra(TreeNode root){ if(root == null){ return; } inorderTra(root.left); list.add(root.val); inorderTra(root.right); return; } }
时间复杂度:O(n),因为遍历的过程中,在每一个结点处进行的工作是常数时间,所以总的时间复杂度为O(n)
空间复杂度:最坏情况下需要空间O(n),平均情况为O(log n)。
为什么是常数时间:因为在遍历的过程中,每一个结点最多经过三次。
空间复杂度和树的高度有关。
2. 基于栈的遍历
与递归类似,只不过是自己使用了栈代替递归。
class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); Stack<TreeNode> tmp = new Stack<>(); TreeNode cur = root; while(cur != null || !tmp.isEmpty()){ while(cur != null){ tmp.push(cur); cur = cur.left; } cur = tmp.pop(); list.add(cur.val); cur = cur.right; } return list; } }
时间复杂度:O(n)。
空间复杂度:O(n)。
3. 莫里斯遍历(线索二叉树)
步骤:
Step 1: 将当前节点 current 初始化为根节点
Step 2: While current 不为空,
若 current 没有左子节点
- 将 current 添加到输出
- 进入右子树,亦即, current = current.right
否则
- 在 current 的左子树中,令 current 成为最右侧节点的右子节点
- 进入左子树,亦即,current = current.left
例子:
1 / \ 2 3 / \ / 4 5 6
首先,1 是根节点,所以将 current 初始化为 1。1 有左子节点 2,current 的左子树是
2 / \ 4 5
在此左子树中最右侧的节点是 5,于是将 current(1) 作为 5 的右子节点。令 current = cuurent.left (current = 2)。
现在二叉树的形状为:
2 / \ 4 5 \ 1 \ 3 / 6
对于 current(2),其左子节点为4,我们可以继续上述过程
4 \ 2 \ 5 \ 1 \ 3 / 6
由于 4 没有左子节点,添加 4 为输出,接着依次添加 2, 5, 1, 3 。节点 3 有左子节点 6,故重复以上过程。
最终的结果是 [4,2,5,1,6,3]。
代码:
class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); TreeNode cur = root; TreeNode tmp = null; while(cur != null){ //判断是否有左孩子 if(cur.left == null){ list.add(cur.val); cur = cur.right; }else{ tmp = cur.left; //找到当前结点左子树的最右孩子 while(tmp.right != null){ tmp = tmp.right; } tmp.right = cur; //将当前结点作为左子树的最右孩子 cur = cur.left; //将当前结点更新为原节点的左孩子 tmp.right.left = null; //将原结点的左孩子置为 null } } return list; } }
时间复杂度:O(n),对于 n 个结点的二叉树有 n-1 条边,每一条边会经过两次,一次为定位结点,一次是寻找前驱结点。
空间复杂度:O(n),只是因为使用了长度为 n 的数组,但在遍历的过程中并未重新申请新的空间。
定位结点:更新当前根结点的过程
寻找前驱结点:一直找最右孩子的过程