重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字
示例 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]
限制:
0 <= 节点个数 <= 5000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof
考虑前序遍历和中序遍历的特点:
- 前序遍历: 遍历结果的第一个节点一定是根节点
- 中序遍历: 根节点的左边一定是左子树, 根节点的右边一定是右子树
基于二叉树前序遍历和后序遍历的这种特点:
我们很快就能找到根节点在中序遍历结果中的位置,并将中序遍历划分为 左子树——根节点——右子树
这种结构
然后根据根节点的索引值可以确定左子树和右子树的长度,那么前序遍历就可以划分为根节点——左子树——右子树
对于左右子树,解决问题的方法没有变化,只是问题的规模变小了,显然我们可以用分治法解决这个问题。
递推参数: root
(根节点在前序遍历的索引值)left
(子树的左边界在中序遍历的索引值)right
(子树的右边界在中序遍历中的索引值)
结束条件: left > right
时,注意不能使用>=
,left = right
时,代表子树只有一个根节点
递推:
-
建立根节点(值为该树前序遍历结果的第一个)
-
划分左右子树(需要查询根节点在中序遍历中的索引)
优化:一般情况下,使用一个
for
循环就可以找到根节点在中序遍历中的位置但是使用
哈希表
进行查询可以将该过程的时间复杂度从O(n)
降至O(1)
-
开始递归,构建子树
!注意:索引位置的确定
首先要确定是在先序遍历中的索引还是在中序遍历中的索引
对于 root
left
right
三个参数
root
要通过先序遍历的内容确定,因此要找到根节点在先序遍历中的索引
left
和right
是用来在中序遍历中划分子树,因此要找left
和right
在中序遍历中的索引值
根节点 | 左边界 | 右边界 | |
---|---|---|---|
左子树 | root+1 | left | i |
右子树 | i-left+root+1 | i+1 | right |
其中右子树的根节点可能不太好理解:
可以理解为:根节点(root)+ 左子树长度(i - left)+ 1
返回值: 递推中建立的根节点
复杂度分析:
时间复杂度 O(N)
空间复杂度 O(N)
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int[] preorder;
HashMap<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for(int i=0; i<inorder.length; i++){
map.put(inorder[i],i);
}
return build(0,0,preorder.length-1);
}
public TreeNode build(int root, int left, int right){
if(left > right){
return null;
}
TreeNode node = new TreeNode(preorder[left]);
int i = map.get(node.val);
node.left = build(root+1,left,i-1);
node.right = build(root+i-left+1, i+1, right);
return node;
}
}