题目描述
给定一棵树的前序遍历 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]
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/co… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解思路
题目给定的是二叉树的前序遍历和中序遍历,根据前序遍历和中序遍历的概念,前序遍历的首节点即为二叉树的根节点,而根节点在中序遍历中的位置的左边的所有节点即为二叉树根节点的左子树的所有节点,右边的所有节点为根节点右子树的所有节点,如图:
而上下对应的前序和中序拆分出来的左子树和右子树部分又可以差分成这样的结构,所以,这显然是个递归问题。 在递归方法中,我们需要递归的是每次重新拆分后得到的左子树的前序遍历集和左子树的中序遍历集,以及右子树的前序遍历集和右子树的中序遍历集。
- 如图可知,我们定义最初的root节点位置为preStart,那么拆分后的左子树前序遍历集的左临界点的位置为preStart+1,右临界点的位置未知,我们暂且设为x,那么右子树前序遍历集的左临界点即为x+1,右临界点定为preEnd。
- 定义最初的中序遍历root左边的左子树集的左临界点位置为inoStart,根据root为前序遍历的第一个节点可以得到root在中序遍历中的位置定为index,那么中序遍历中左子树节点集的右临界点位置为index-1,中序遍历中右子树左临界点位置为index+1,右临界点为inoEnd。
- 根据前序和中序遍历中左右子树集的长度相等原则,我们可以得出等式 x-(preStart+1)=index-1-inoStart,整理得出x=index-inoStart+preStart,那么前序遍历中右子树的左临界点的位置为index-inoStart+preStart+1
- 这样每次拆分后的位置我们都可以知道,根据这些位置我们可以得到每次拆分后前序遍历中的左子树集和中序遍历中的左子树集,以及前序遍历中的右子树集及中序遍历中的右子树集。
- 直到preStart-preEnd<0,说明无节点可以拆分,递归结束。
题解代码
/**
* 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) {
return buildNode(preorder,0,preorder.length - 1,inorder,0);
};
///
function buildNode(preorder, preStart, preEnd, inorder, inoStart){
if(preStart > preEnd) return null;
let rootVal = preorder[preStart];//根节点就是前序遍历的第一个节点
let index = inorder.indexOf(rootVal);//找到根节点在中序遍历中的位置
let preStartLeft = preStart + 1;//找到前序遍历中左树首位节点的位置
let preEndLeft = index - inoStart + preStart;//找到前序遍历中左树末位节点的位置
let inoStartLeft = inoStart//找到中序遍历中左树首节点位置
let preStartRight = index - inoStart + preStart + 1;//找到前序遍历中右树首位节点的位置
let preEndRight = preEnd;//找到前序遍历中右树末位节点的位置
let inoStartRight = index + 1//找到中序遍历中右树首节点位置
let root = new TreeNode(rootVal);//生成根节点
root.left = buildNode(preorder,
preStartLeft,
preEndLeft,
inorder,
inoStartLeft);//生成根节点的左节点
root.right = buildNode(preorder,
preStartRight,
preEndRight,
inorder,
inoStartRight);//生成根节点的右节点
return root;
}