给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的前序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
☞ 首先要知道前中序遍历是什么?
如果一个二叉树只有3个节点,根节点1,左节点2,右节点3,那么先收集根节点的结果,然后处理左子节点的结果,再右子节点,即[1, 2, 3],此时的遍历为前序遍历,优先遍历根节点。如果是中序遍历,就是先左节点然后根节点,最后右节点,即[2, 1, 3],优先处理左节点。
思路
知道前序数遍历和中序遍历是什么,很容易从扫描结果数组中找到规律。
- 前序遍历的第一个元素就是二叉树的根节点,即
preorder[0]的值为二叉树的根节点的值 - 从
preorder数组中,是无法知道索引0之后的值哪些是左子数的值,哪些是右子树的值。但是可以从inorder中获取。先找到preorder[0]的值在inorder中对应的位置索引,比其小的就是左子树的值,比其大的就是右子树的值。 - 通过当前前序和中序数组可以生成左右子树对应的前序和中序数组,然后可以继续调用
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
}