「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。
题目
给定两个整数数组 preorder 和 inorder ,其中 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 保证 为二叉树的中序遍历序列
思路
要从 先序遍历
和 中序遍历
的结果中,反向构建出二叉树,首先需要了解,这两个方式遍历之后形成的结果是什么结构,可以先看这篇 二叉树的中、先、后序遍历
先序遍历
会形成如下数据结构
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
中序遍历
会形成如下数据结构
[ [左子树的前序遍历结果], 根节点, [右子树的前序遍历结果] ]
如果想要构建一个二叉树,首先需要根节点,那很容易发现 先序遍历
的第一个元素,就是根节点。
有了根节点,我们需要左子树和右子树,很明显,在 中序遍历
中,如果找到根节点的位置,那它的左边就是左子树,右边就是右子树
现在有:
根节点
先序遍历:[ [左子树的前序遍历结果], [右子树的前序遍历结果] ]
中序遍历:[ [左子树的前序遍历结果], [右子树的前序遍历结果] ]
如果,能够将先序遍历中的左子树拆出来,会得到和之前完全一样的两组结果:左子树的先序、中序遍历,右子树的先序、中序遍历,再递归执行上述过程就行。
递归完整代码如下
/**
* 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
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
const buildNode = (preStart, preEnd, inStart, inEnd) => {
if (preStart > preEnd) return null;
const rootVal = preorder[preStart];
// 根节点在中序遍历中的位置
const rootIdx = inorder.indexOf(rootVal);
// 左子树大小
const leftSize = rootIdx - inStart;
const root = new TreeNode(rootVal);
root.left = buildNode(preStart + 1, preStart + leftSize, inStart, rootIdx - 1);
root.right = buildNode(preStart + leftSize + 1, preEnd, rootIdx + 1, inEnd)
return root;
}
// 构建根节点
return buildNode(0, preorder.length - 1, 0, inorder.length - 1)
};
思考: 上面的解法,用到了题目提示中的一个关键要点 preorder 和 inorder 均 无重复 元素
,所以在 中序遍历中
寻找根节点时采用了 indexOf
的捷径,但这里其实每次都去寻找根节点,时间复杂度为 O(n)
,可以采取将 中序遍历
的结果转换成哈希结构,那么在递归时,每次寻找根节点的时间复杂度就会降为 O(1)
,典型的空间换时间
优化后部分代码
const rootMap = {};
for(let i = 0; i < inorder.length; i++) {
rootMap[inorder[i]] = i;
}
const buildNode = (preStart, preEnd, inStart, inEnd) => {
// ...
// 根节点在中序遍历中的位置
const rootIdx = rootMap[rootVal];
// ...
}
小结
二叉树,学完了如何遍历,就该学学如何从遍历后的结果中,反向构建出二叉树了
LeetCode 👉 HOT 100 👉 从前序与中序遍历序列构造二叉树 - 中等题 ✅
合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍
如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄