LeetCode106.从中序与后序遍历序列构造二叉树

297 阅读3分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

从中序与后序遍历序列构造二叉树

根据一棵树的中序遍历与后序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:

image.png

解法

首先中序遍历的遍历次序是左中右,后序遍历的遍历次序是左右中,根据这两个次序,我们可以发现一些规律,我们从一个普通子树画图讲解一下这些规律

image.png

这颗子树是没有重复元素的,我们能发现:

  1. 后序遍历数组的最后一个元素是根节点的值,我们可以根据这个构建根节点
  2. 中序遍历数组中,根节点前面的元素是其左子树各节点的val值(也是按照中序遍历次序排列),根节点后面的元素是其右子树各节点的val值(也是按照中序遍历次序排列),所以我们从后序遍历中得到根节点的val之后,就可以找到根节点在中序遍历数组中的位置,以此分割左子树和右子树的中序数组
  3. 一棵树中序遍历和后序遍历的数组长度是一样的,我们可以从分割好的左子树和右子树的中序遍历数组,得到数组长度,分割左子树和右子树的后序遍历数组

依照以上三点,我们可以利用中序遍历数组和后序遍历数组递归构建完整的二叉树,每次递归,都构建当前中序和后序数组的根节点,并返回给父节点,用以父节点的left和right构建,递归的终止条件是中序或者后序数组为空(证明已经构建到了当前路径的叶子结点)

代码如下

var buildTree = function(inorder, postorder) {
    const buildSubTree = function(inorder, postorder) {
        // 如果传入的中序遍历数组长度为0,证明已经递归到了叶子节点
        if (inorder.length === 0) return null;
        // 构建当前迭代的根节点
        let rootNode = new TreeNode(postorder.pop());
        // 分割根节点的中序遍历数组形成根节点左子树的中序遍历数组
        let leftInorder = inorder.slice(0,inorder.indexOf(rootNode.val));
        // 分割根节点的后序遍历数组形成根节点左子树的后序序遍历数组
        let leftPostorder = postorder.slice(0,leftInorder.length);
        // 分割根节点的中序遍历数组形成根节点右子树的中序遍历数组
        let rightInorder = inorder.slice(inorder.indexOf(rootNode.val)+1);
        // 分割根节点的后序遍历数组形成根节点右子树的后序序遍历数组
        let rightPostorder = postorder.slice(leftInorder.length)
        // 构建当前根节点的左子节点并做连接
        rootNode.left = buildSubTree(leftInorder, leftPostorder);
        // 构建当前根节点的右子节点并做连接
        rootNode.right = buildSubTree(rightInorder, rightPostorder);
        // 返回当前根节点供其父节点做节点连接
        return rootNode;
    }
    // 如果初始的中序遍历数组长度为0,证明该树为空
    if (inorder.length === 0) return null;
    // 递归构建树
    let root = buildSubTree(inorder, postorder);
    return root;
};