二叉树的中序遍历
涉及知识:二叉树的遍历
定义结点的结构:
/**
* 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 的数组,但在遍历的过程中并未重新申请新的空间。
定位结点:更新当前根结点的过程
寻找前驱结点:一直找最右孩子的过程