二叉树的中序遍历

题目链接

涉及知识:二叉树的遍历

定义结点的结构:

/**
 * 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 的数组,但在遍历的过程中并未重新申请新的空间。

定位结点:更新当前根结点的过程

寻找前驱结点:一直找最右孩子的过程

posted @ 2019-08-09 13:44  telankesi  阅读(435)  评论(0编辑  收藏  举报