一日一练:从前序与中序遍历序列构造二叉树

99 阅读2分钟

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

首先要知道前中序遍历是什么

如果一个二叉树只有3个节点,根节点1,左节点2,右节点3,那么先收集根节点的结果,然后处理左子节点的结果,再右子节点,即[1, 2, 3],此时的遍历为前序遍历,优先遍历根节点。如果是中序遍历,就是先左节点然后根节点,最后右节点,即[2, 1, 3]优先处理左节点

思路

知道前序数遍历和中序遍历是什么,很容易从扫描结果数组中找到规律。

  1. 前序遍历的第一个元素就是二叉树的根节点,即preorder[0]的值为二叉树的根节点的值
  2. preorder数组中,是无法知道索引0之后的值哪些是左子数的值,哪些是右子树的值。但是可以从inorder中获取。先找到preorder[0]的值在inorder中对应的位置索引,比其小的就是左子树的值,比其大的就是右子树的值。
  3. 通过当前前序和中序数组可以生成左右子树对应的前序和中序数组,然后可以继续调用buildTree去生成左子树和右子树。很明显是递归的思路

技巧

这里有一个技巧,就是修改题目本来的定义,增加 父节点 和 区分左右节点 的参数。

代码

/**
 * @function buildTree
 * @param preorder { number[] } 前序遍历数组
 * @param inorder { number[] } 中序遍历数组
 * @param parent { (TreeNode| null)= } 父节点
 * @param isLeft { boolean= } 是否为左节点,true为左节点,false为右节点
 * @returns { Tree | null } 构建的二叉树
 **/

function buildTree(
  preorder: number[],
  inorder: number[],
  parent?: TreeNode | null,
  isLeft?: boolean
): TreeNode | null {
  // 如果前序遍历数组为空 直接返回null
  if (preorder.length === 0) {
    return null 
  }

  if (parent) {
    // 构建左或者右节点,同时更新parnet
    parent = parent[isLeft ? 'left' : 'right'] = new TreeNode(
      preorder[0],
      null,
      null
    )
  } else {
    // root节点
    parent = new TreeNode(preorder[0], null, null)
  }

  // 找到当前构建的节点再inorder中的位置
  // 左侧的就是该节点的左子树的节点
  // 右侧的就是该节点的由子树的节点
  const idx = inorder.findIndex((v) => v === preorder[0])
  // 递归构建左右子树
  buildTree(preorder.slice(1, 1 + idx), inorder.slice(0, idx), parent, true)
  buildTree(preorder.slice(1 + idx), inorder.slice(idx + 1), parent, false)

  return parent
}