Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目分析
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
思路讲解
- 根据前序遍历的顺序,获取树的根节点,根据中序遍历,获取到根节点的左右⼦树。
- 可以先构建对应的根节点,然后去判断这个节点的左右⼦树部分,获取左右⼦树的根节点,再去构建。
- 重复上述操作直到将整个树全部构建出来。
示例
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
输入: preorder = [-1], inorder = [-1]
输出: [-1]
代码
/*
* @lc app=leetcode.cn id=105 lang=javascript
*
* [105] 从前序与中序遍历序列构造二叉树
*/
// @lc code=start
/**
* 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 中序遍历数组
* 已知:给了我们一共前序遍历数组和中序遍历数组,前序遍历:根左右,中序遍历:左根右
* 根据遍历的特性可知,前序遍历数组的第一个元素,是整个树的根结点,我们就能获取到根结点的值
* 在中序遍历数组中,找到根结点的位置mid,就可以分隔左右子树——左子树(istart ~ mid - 1), 右子树(mid + 1 ~ inorder.length - 1)
* 获取左子树的节点个数leftNum,用来获取在前序遍历数组中左子树和右子树的分割点
* 左子树:pStart + 1 ~ pStart + leftNum, 右子树:pStart + leftNum + 1 ~ preorder.length - 1
* 按上述步骤分别去构建左右子树
* @return {TreeNode}
*/
var buildTree = function (preorder, inorder) {
let map = new Map()
for (let i = 0; i < inorder.length; i++) {
map.set(inorder[i], i) // 将值与坐标映射,用法:拿到值去获取坐标
}
const helper = function (pStart, pEnd, iStart, iEnd) { // pStart:前序开始坐标, pEnd:前序结束坐标, iStart:中序开始坐标, iEnd:中序结束坐标
/** 证明我们的前序遍历数组已经使用完毕 */
if (pStart > pEnd) return null // 如果头索引大于尾索引,结束
let rootVal = preorder[pStart] // 获取根结点的值
let root = new TreeNode(rootVal) // 构造一个根结点
// 找子节点:从中序遍历中找到根结点,然后再去匹配它左侧的值跟右侧的值
/** 获取根结点在中序遍历数组中的索引位置,来分隔左右子树 */
let mid = map.get(rootVal) // mid左侧为左子树 右侧为右子树
/** 计算左子树的节点个数,用来在前序遍历数组确定左子树结束的位置 */
let leftNum = mid - iStart // 左子树一共有多少节点:终点mid(根结点的坐标) - 开始值的坐标
/** 递归的构建左子树 */
root.left = helper(pStart + 1, pStart + leftNum, iStart, mid - 1)
/** 递归的构建右子树 */
root.right = helper(pStart + leftNum + 1, pEnd, mid + 1, iEnd)
return root
}
return helper(0, preorder.length -1, 0, inorder.length - 1)
};
// @lc code=end