题目
105. 从前序与中序遍历序列构造二叉树
给定一棵树的前序遍历 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]
思路:
递归法。
首先,我们可以知道
-
前序遍历的头节点是根节点,然后是左子树,然后是右子树
-
中序遍历的开始一段是左子树,然后是根节点,然后是右子树
我们要弄清楚的就是,
-
具体前序遍历的左子树和右子树的准确的起始和结束位置
-
具体中序遍历的左子树的结束位置,根节点的位置,以及右子树的起始结束位置
那么我们假设
-
前序遍历的根节点下标为
pre_left, 右子树的末尾下表为pre_right -
中序遍历的左子树开始为
in_left,根节点为in_root,右子树末尾为in_right -
根据
pre_left的值,可以通过中序遍历indexOf找到中序遍历根节点in_root的下标。 -
然后得到中序遍历左子树末尾的下表为
in_root-1,右子树起始位in_root+1 -
由于前序和中序的左子树的长度是一致的,那么可以得到前序遍历的左子树起始位pre_left+1,末尾为
in_root - in_left - pre_left,右子树的起始为in_root - in_left - pre_left+1
然后我们就可以通过递归函数的去分别构造左子树和右子树
具体的创建子树的递归函数逻辑如下
-
递归函数接受参数为,完整的前序遍历和中序遍历数组
preorder、inorder,子树的前序遍历起始pre_left、末尾pre_right,子树的中序遍历起始in_left、末尾in_right -
递归结束条件为,
pre_left > pre_right,这代表子树一个节点也没有了,直接返回null即可 -
通过子树前序遍历起始位置
pre_left,创建根节点root = new TreeNode(preorder[pre_left]) -
通过
pre_left对应的值,找到中序遍历根节点位置in_root = inorder.indexOf(preorder[pre_left]) -
通过中序遍历起始
in_left和根节点位置in_root计算出新的左子树的长度size = in_root - in_left -
那么可以算出新的左子树的前序起始
pre_left+1、末尾pre_left + size,中序起始in_left、末尾in_root-1 -
新的右子树的前序起始
pre_left + size + 1,末尾pre_rifht,中序起始in_root + 1,末尾in_right -
递归调用自身去创建新的左子树和右子树,并挂在到所对应的
root节点上 -
最终返回
root节点 -
第一次调用该递归函数的入参分别为
preorder,inorder(这俩已知),前序起始0,末尾preorder.length-1,中序起始0,末尾inorder.length-1
代码如下:
* @param {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
// 算出节点数量len
let len = preorder.length;
return myBuildTree(preorder, inorder, 0, len-1, 0, len-1)
};
function myBuildTree(preorder, inorder, pre_left, pre_right, in_left, in_right) {
// 若pre_left > pre_right,说明子树为空,直接返回null
if(pre_left > pre_right){
return null;
}
// 通过pre_left的值创建子树的root节点
let root = new TreeNode(preorder[pre_left]);
// 通过pre_left的值,在inorder中找出他的下标
let in_root = inorder.indexOf(preorder[pre_left]);
// 算出左子树的长度,这样即可定位出所有需要的下标
let size = in_root - in_left;
// 递归创建左子树,并连接到当前根节点
root.left = myBuildTree(preorder, inorder, pre_left + 1, pre_left + size, in_left, in_root - 1);
// 递归创建右子树,并连接到当前根节点
root.right = myBuildTree(preorder, inorder, pre_left + size + 1,pre_right,in_root + 1, in_right)
return root
}