LeetCode 👉 HOT 100 👉 从前序与中序遍历序列构造二叉树 - 中等题

105 阅读2分钟

「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。

题目

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例1

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]

输出: [3,9,20,null,null,15,7]

示例2

输入: preorder = [-1], inorder = [-1]

输出: [-1]

提示:

1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列

思路

要从 先序遍历中序遍历 的结果中,反向构建出二叉树,首先需要了解,这两个方式遍历之后形成的结果是什么结构,可以先看这篇 二叉树的中、先、后序遍历

先序遍历 会形成如下数据结构

[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]

中序遍历 会形成如下数据结构

[ [左子树的前序遍历结果], 根节点, [右子树的前序遍历结果] ]

如果想要构建一个二叉树,首先需要根节点,那很容易发现 先序遍历 的第一个元素,就是根节点。

有了根节点,我们需要左子树和右子树,很明显,在 中序遍历 中,如果找到根节点的位置,那它的左边就是左子树,右边就是右子树

现在有:

根节点
先序遍历:[ [左子树的前序遍历结果], [右子树的前序遍历结果] ]
中序遍历:[ [左子树的前序遍历结果], [右子树的前序遍历结果] ]

如果,能够将先序遍历中的左子树拆出来,会得到和之前完全一样的两组结果:左子树的先序、中序遍历,右子树的先序、中序遍历,再递归执行上述过程就行。

递归完整代码如下

    /**
     * Definition for a binary tree node.
     * function TreeNode(val, left, right) {
     *     this.val = (val===undefined ? 0 : val)
     *     this.left = (left===undefined ? null : left)
     *     this.right = (right===undefined ? null : right)
     * }
     */
    //  `先序遍历` 会形成如下数据结构

    //  [ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]

    // `中序遍历` 会形成如下数据结构

    //  [ [左子树的前序遍历结果], 根节点, [右子树的前序遍历结果] ]
    /**
     * @param {number[]} preorder
     * @param {number[]} inorder
     * @return {TreeNode}
     */
    var buildTree = function(preorder, inorder) {
        const buildNode = (preStart, preEnd, inStart, inEnd) => {

            if (preStart > preEnd) return null;

            const rootVal = preorder[preStart];
            // 根节点在中序遍历中的位置
            const rootIdx = inorder.indexOf(rootVal);
        
            // 左子树大小
            const leftSize = rootIdx - inStart;

            const root = new TreeNode(rootVal);

            root.left = buildNode(preStart + 1, preStart + leftSize, inStart, rootIdx - 1);

            root.right = buildNode(preStart + leftSize + 1, preEnd, rootIdx + 1, inEnd)
            return root;

        }
        // 构建根节点
        return buildNode(0, preorder.length - 1, 0, inorder.length - 1)
    };

思考: 上面的解法,用到了题目提示中的一个关键要点 preorder 和 inorder 均 无重复 元素,所以在 中序遍历中 寻找根节点时采用了 indexOf 的捷径,但这里其实每次都去寻找根节点,时间复杂度为 O(n),可以采取将 中序遍历 的结果转换成哈希结构,那么在递归时,每次寻找根节点的时间复杂度就会降为 O(1),典型的空间换时间

优化后部分代码

    
    const rootMap = {};

    for(let i = 0; i < inorder.length; i++) {
        rootMap[inorder[i]] = i;
    }

    const buildNode = (preStart, preEnd, inStart, inEnd) => {

        // ...
        
        // 根节点在中序遍历中的位置
        const rootIdx = rootMap[rootVal];
        
        // ...
    }

小结

二叉树,学完了如何遍历,就该学学如何从遍历后的结果中,反向构建出二叉树了

LeetCode 👉 HOT 100 👉 从前序与中序遍历序列构造二叉树 - 中等题

合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍

如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄