剑指 Offer 07. 重建二叉树
题目描述:
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例:
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
限制:
0 <= 节点个数 <= 5000
解题思路:
方法一:递归
二叉树前序遍历:根、左、右
二叉树中序遍历:左、根、右
前序遍历的第一个节点是根节点,只要找到根节点在中序遍历中的位置m,在中序遍历中根节点所在位置m前的节点都位于左子树,根节点之后的节点都位于右子树,由此可以知道左右子树各有多少个节点。
因为中序遍历和前序遍历的节点个数都是一样的,通过中序遍历知道左子树和右子树的节点数量后可以根据节点数量得到前序遍历中的左子树和右子树的分界,因此可以进一步得到左子树和右子树各自的前序遍历和中序遍历,可以通过递归的方式,重建左子树和右子树,然后重建整个二叉树。
使用map
存储中序遍历的元素和下标,递归方法的基准情形有两个:判断前序遍历的下标范围的开始和结束,若开始大于结束,则当前的二叉树中没有节点,返回空值 null。若开始等于结束,则当前的二叉树中恰好有一个节点,根据节点值创建该节点作为根节点并返回。
若开始小于结束,则当前的二叉树中有多个节点。在中序遍历中得到根节点的位置,从而得到左子树和右子树各自的下标范围和节点数量,知道节点数量后,在前序遍历中即可得到左子树和右子树各自的下标范围,然后递归重建左子树和右子树,并将左右子树的根节点分别作为当前根节点的左右子节点。
- 时间复杂度:O(n)。对于每个节点都有创建过程以及根据左右子树重建过程。
- 空间复杂度:O(n)。存储整棵树的开销。
class Solution {
public:
map<int, int> m;
vector<int> Preorder;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n=inorder.size();
for(int i=0; i<n; ++i)
m[inorder[i]] = i;
Preorder = preorder;
return dfs(0, n-1, 0);
}
// preleft-preright表示重建前序遍历中索引preleft-preright的子树
// 该子树从中序遍历下标为inleft的位置开始
TreeNode* dfs(int preleft, int preright, int inleft){
if(preleft > preright) return nullptr;
if(preleft == preright) return new TreeNode(Preorder[preleft]);
int index = m[Preorder[preleft]];
TreeNode *head = new TreeNode(Preorder[preleft]);
//以先序遍历中preleft+1~preleft+index-inleft构建的树为当前节点左子树
// index-inleft表示该左子树的节点个数
head->left = dfs(preleft+1, preleft+index-inleft, inleft);
head->right = dfs(preleft+index-inleft+1, preright, index+1);
return head;
}
};