给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。
示例 1:
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 <= 3000inorder.length == preorder.length-3000 <= preorder[i], inorder[i] <= 3000preorder和inorder均无重复元素inorder均出现在preorderpreorder保证为二叉树的前序遍历序列inorder保证为二叉树的中序遍历序列
本题解题思路如下:
- 前序遍历的顺序为根左右,中序遍历的顺序为左根右,所以
preorder[0]为当前子树根节点的值 - 查找该值在
inorder中的位置,其左边即为左子树中序遍历序列,右边为右子树中序遍历序列 - 获取到左右子树中序遍历后,因为子树前序遍历和中序遍历的结果只是顺序不同,但是序列长度是相同的,所以可以根据左子树中序遍历序列长度去
preorder获取左子树前序遍历序列,剩余部分即为右子树前序遍历序列 - 然后就可以通过根节点的值创建根节点,根据左右子树的前序、中序遍历序列递归创建左右子树
- 最后返回二叉树的根节点即可
动画演示如下:
第一版代码如下:
var buildTree = function(preorder, inorder) {
// 如果前序遍历序列为空,返回空
if(preorder.length === 0) return null;
// 创建子树根节点
const root = new TreeNode(preorder.shift()),
// 获取根节点值在中序遍历序列的下标
ind = inorder.indexOf(root.val);
// 通过根节点值在中序遍历序列的下标,截取左子树前序遍历,中序遍历序列,创建左子树
root.left = buildTree(preorder.splice(0,ind),inorder.splice(0,ind));
// 根据剩余部分截取右子树前序遍历,中序遍历序列,创建右子树
root.right = buildTree(preorder,inorder.splice(1));
// 返回根节点
return root;
};
这一版代码提交通过后,只击败了 50% 左右的用户,是因为在递归过过程需要截取左右子树的前序、中序遍历序列,这样的操作增加了执行用时和空间复杂度。
这里我们可以通过维护当前子树前序遍历序列开始结束下标,及中序遍历序列开始结束下标代替截取前序、中序遍历序列的操作,同样达到递归构建左右子树的目的。
优化后代码如下:
var buildTree = function(preorder, inorder) {
// 传入前序遍历序列开始下标,结束下标,中序遍历序列开始下标
function build(preStart,preEnd,inStart){
// 如果前序遍历序列开始下标大于结束下标,说明区间处理完成,返回 null
if(preStart>preEnd) return null;
// 通过前序遍历开始下标值构建根节点
const root = new TreeNode(preorder[preStart]),
// 获取根节点值在中序遍历序列的下标
ind = inorder.indexOf(root.val),
// 获取中序遍历左子树的长度
leftSize = ind-inStart;
// 递归的构建左右子树
root.left = build(preStart+1,preStart+leftSize,inStart);
root.right = build(preStart+leftSize+1,preEnd,ind+1);
return root;
}
// 返回构建后二叉树的根节点
return build(0,preorder.length-1,0)
};
至此我们就完成了 leetcode-105-从前序与中序遍历序列构造二叉树
如有任何问题或建议,欢迎留言讨论!