[路飞]_程序员必刷力扣题: 105. 从前序与中序遍历序列构造二叉树

304 阅读2分钟

105. 从前序与中序遍历序列构造二叉树

给定一棵树的前序遍历 preorder 与中序遍历  inorder。请构造二叉树并返回其根节点。

示例 1:

image.png

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

提示:

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

递归遍历

思路

这道题目中我们要跟据一棵树的前序遍历和中序遍历来还原树

那么我们可以用递归的思想来做

条件一:首先我们可以通过前序遍历来找到树的根节点root的值

条件二:接下来我们可以在后续遍历中找到root节点的位置信息rootIndex

条件三:无论左子树的前序遍历还是中序遍历,他们的数量一定是相同的

从以上两个条件中,我们可以得到一个信息:

前序遍历 [root,leftChildPre,rightChildPre]

  • leftChildPre:左子树的前序遍历
  • rightChildPre:右子树的前序遍历

中序遍历 [leftChildOrder,root,rightChildOrder]

  • leftChildOrder:左子树的中序遍历
  • rightChildOrder:右子树的中序遍历

那么我们就可以这么处理:

  • 用左子树的前序遍历leftChildPre和中序遍历leftChildOrder来生成左子树
  • 用右子树的前序遍历rightChildPre和中序遍历rightChildOrder来生成右子树
  • 然后把左子树和右子树分别拼接在root节点上

至此,思路就完毕了

具体实现看代码注释

var buildTree = function (preorder, inorder) {
    //通过前序遍历的首节点来获取根节点
    var rootNode = preorder[0]
    //通过findIndex来获取中序遍历中rootNode的位置信息
    var rootIndex = inorder.findIndex(item => item === rootNode)
    //根据rootIndex来获取左子树的前序遍历结果
    var leftChildPre = preorder.slice(1, 1 + rootIndex)
    //同理获取右子树的前序遍历结果
    var rightChildPre = preorder.slice(1 + rootIndex)
    //根据rootIndex来获取左子树的中序遍历结果
    var leftChildOrder = inorder.slice(0, rootIndex)
    //同理获取右子树的中序遍历结果
    var rightChildOrder = inorder.slice(1 + rootIndex)
    //最后声明两个变量来保存左子树和右子树的还原结果
    var leftChildRoot, rightChildRoot
    //重点,递归的出口
    //如果左子树的节点数量小于等于1,那么只有一个节点或者为null
    if(leftChildPre.length <= 1){
        //如果左子树有节点则直接生成一颗单节点的树 , 否则为null
        leftChildRoot = leftChildPre.length > 0 ? new TreeNode(leftChildPre[0]) : null
    }else{
        //如果节点数量大于1则需要继续递归处理
        leftChildRoot = buildTree(leftChildPre, leftChildOrder)
    }
    //同上
    if(rightChildPre.length <= 1){
        rightChildRoot = rightChildPre.length > 0 ? new TreeNode(rightChildPre[0]) : null
    }else{
        rightChildRoot = buildTree(rightChildPre, rightChildOrder)
    }
    //最后只需要把左右子树衔接在根节点下即可
    var root = new TreeNode(rootNode, leftChildRoot, rightChildRoot)
    
    return root
};