Binary Tree Inorder Traversal ---LeetCode

https://leetcode.com/problems/binary-tree-inorder-traversal/

解题思路:

这道题是中序遍历一棵二叉树。

首先回顾一下二叉树的遍历:
可以简单分为两种方法,深度优先遍历和广度优先遍历。

  • 深度优先遍历:L、D、R分别表示遍历左子树、访问根结点和遍历右子树。

    • 先序遍历(preorder traversal)二叉树的顺序是DLR,
    • 中序遍历(inorder traversal)二叉树的顺序是LDR,
    • 后序遍历(postorder traversal)二叉树的顺序是LRD。
  • 广度优先遍历:又称为层序遍历(level traversal)二叉树,顾名思义,一层一层的遍历二叉树。

这些方法的时间复杂度都是O(n),n为结点个数。


根据中序遍历的定义,很容易推出中序遍历一棵二叉树的顺序:

  • 遍历左子树
  • 遍历根节点,并记录根节点的值。
  • 遍历右子树

因此,用递归可以简单实现这道题。需要遍历整棵树一次,所以时间复杂度是 O(n)。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        inorder(root, result);
        return result;
    }
    public void inorder(TreeNode root, List<Integer> result) {
        if (root == null) return ;

        inorder(root.left, result);
        result.add(root.val);
        inorder(root.right, result);
    }
}

有递归就有迭代。下面同样用迭代的方式实现一下。

这里写图片描述

例如上面这棵二叉树,首先分析一下递归调用的顺序:

首先递归到左子树的左边叶子节点 D,它没有左子节点,接着记录节点 D 的值,接着递归 D 的右子节点,发现它也没有右子节点所以再返回到节点 B。记录节点 B 的值,并递归 B 的右子节点 E,从 E 开始又访问 E 的左右子树。即:相当于把 E 看成了新一轮的根节点。遍历完并记录完 E 的左右子树后,也就遍历完了 B 的右子树,就可以返回到节点 A 了,至此已经访问完毕 A 的左子树,以同样的方式再访问 A 的右子树。

可以发现,如果我们维护一个栈,将左边的节点(A,B,D)都入栈。接着一个一个弹出,记录节点的值,接着将它们的右子节点当做新一轮循环的根节点,再将左边的节点入栈。以此类推,就可以达到和递归一样的效果。

说起来很拗口,看一下代码吧~

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();

        if (root == null) return result;
        Stack<TreeNode> stack = new Stack<>();

        while (root != null || !stack.isEmpty()) {
            if (root != null) {
                stack.push(root);
                root = root.left;
            } else {
                root = stack.pop();
                result.add(root.val);
                root = root.right;
            }
        }
        return result;
    }
}

同样,上面的代码可以在空间上优化一下。用到了 Morris Traversal 的方法。这个方法是基于二叉线索树的。

首先回顾一下二叉线索树:

“一个二叉树通过如下的方法“穿起来”:所有应该为空的右孩子指针指向该节点在中序序列中的后继,所有应该为空的左孩子指针指向该节点的中序序列的前驱。”

看下面这张图就明白了,图片来自WiKipedia。
这里写图片描述

以节点 E 为例,节点 E 的左右子节点本都为空。在二叉线索树中,它的左节点指向了 D,即中序遍历中的前驱结点;右节点指向了 F,即中序遍历中的后继结点。

有了二叉线索树的概念,可以更好地说明 Morris Traversal 这个方法了。在 Morris Traversal 方法中,将叶子节点的右指针与它中序遍历的后即结点建立链接,以便返回节点进行后续遍历。当遍历完毕后再解除绑定,使二叉树恢复原样。

Morris Traversal 的代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();

        if (root == null) return result;
        TreeNode curr = root;

        while (curr != null) {
            if (curr.left == null) {
                result.add(curr.val);
                curr = curr.right;
            } else {
                TreeNode prev = curr.left;
                while (prev.right != null && prev.right != curr)
                    prev = prev.right;

                if (prev.right == null) {
                    prev.right = curr;
                    curr = curr.left;
                } else {
                    prev.right = null;
                    result.add(curr.val);
                    curr = curr.right;
                }
            }
        }
        return result;
    }
}

https://www.youtube.com/watch?v=wGXB9OWhPTg 这个视频有对 Morris Traversal 比较详细的解读~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值