「前端刷题」105. 从前序与中序遍历序列构造二叉树

87 阅读2分钟

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

题目

链接:leetcode-cn.com/problems/co… 给定两个整数数组 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 保证 为二叉树的中序遍历序列

思路

从前序和中序中寻找到二叉树结构
方法:递归,从最顶层树节点开始,可以发现每个node.val=当前的值,left=get左侧子树的最顶层节点,right=右侧子树的最顶层节点,可以依次递归下去;
思路:
可以通过观察树的结构特点,可以发现,树的顶点永远在前序的第一个元素,而找到这个顶点在中序数组中的位置,可以发现,树的左侧子树对应的数值
永远在中序数组的左边,右侧的子树的值永远在中序数组对应顶点的右边,那么我们就可以找到左侧子树的中序和前序数组,进入下一个递归,同样右侧子树的中序和前序

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
var buildTree = function(preorder, inorder) {
    var i = 0, tree = new TreeNode, treemap = {}, premap = {},inMap = {}, l = inorder.length;
    while (i < l) {
        premap[preorder[i]] = i;
        inMap[inorder[i]] = i;
        i++;
    }
    if(l ==0) return null;
    function getTop (i0,i1,j0,j1) {
        if(i0<0 || i1<0||j0>=l||j1>=l || i0>j0||  i1>j1) {
            return null;
        }
        var val = preorder[i0];
        var tree = new TreeNode(val);
        var left0 = i0+1, left1 = inMap[val] - 1;
            var l1 = j1 - inMap[val];
            tree.left = getTop(i0+1, i1,i0+1 + left1 - i1, left1);
            tree.right = getTop(j0 - l1 + 1, inMap[val] + 1, j0, j1);
        return tree;
    }
    return getTop(0,0,l -1, l-1);
};

其他思路

  • 前序遍历,根->左->右,中序遍历左->根->右。
  • 即前序遍历为先找根节点,再遍历左节点,然后遍历右节点。中序遍历先遍历左节点,然后根节点,最后遍历右节点。
  • 以题中例子为例,preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]。
  • 根节点为preorder[0] = 3; inorder中inorder[1] = 3;所以inorder中 i < 1的都为左节点,i > 1的都为右节点。
  • 那么可得左节点个数为 i - 0 = i,右节点个数为inorder.length - i - 1。
  • 根据以上结果,我们可以获得preorder中,1 ~ i为左节点,i ~ inorder.length为右节点。

根据这个规律,我们可以分割二叉树,将问题转为分别找左子树和右子树的根结点。

var buildTree = function(preorder, inorder) {
    if(preorder.length === 0 || inorder.length === 0) {
        return null;
    }
    // 根据前序数组的第一个元素,就可以确定根节点
    const root = new TreeNode(preorder[0]);
    for(let i = 0;i < preorder.length; i++) {
        // 用preorder[0]去中序数组中查找对应的元素
        if(preorder[0] == inorder[i]) {
            // 将前序数组分成左右两半
            const pre_left = preorder.slice(1, i + 1);
            const pre_right = preorder.slice(i + 1, preorder.length);
            // 将中序数组分成左右两半
            const in_left = inorder.slice(0, i);
            const in_right = inorder.slice(i + 1, inorder.length);
            // 之后递归的处理前序数组的左边部分和中序数组的左边部分
            root.left = buildTree(pre_left,in_left);
            // 递归处理前序数组右边部分和中序数组右边部分
            root.right = buildTree(pre_right,in_right);
            break;
        }
    }
    return root;
};