class Solution {
public:
// 保存:节点值 -> 中序遍历下标
// 方便快速找到根节点在中序中的位置
unordered_map<int,int> index;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 建立中序哈希表
for(int i = 0; i < n; i++){
index[inorder[i]] = i;
}
// 递归构造整棵树
return build(preorder, inorder,
0, n - 1,
0, n - 1);
}
/*
pre_left ~ pre_right:
当前树在前序遍历中的范围
in_left ~ in_right:
当前树在中序遍历中的范围
*/
TreeNode* build(vector<int>& preorder,
vector<int>& inorder,
int pre_left,
int pre_right,
int in_left,
int in_right){
// 当前范围没有节点
if(pre_left > pre_right){
return nullptr;
}
// 前序第一个节点一定是根节点
int pre_root = pre_left;
// 找到根节点在中序中的位置
int in_root = index[preorder[pre_root]];
// 创建当前根节点
TreeNode* root = new TreeNode(preorder[pre_root]);
// 中序中根节点左边就是左子树
// 计算左子树节点数量
int left_size = in_root - in_left;
// 构造左子树
root->left = build(preorder,
inorder,
pre_left + 1,
pre_left + left_size,
in_left,
in_root - 1);
// 构造右子树
root->right = build(preorder,
inorder,
pre_left + left_size + 1,
pre_right,
in_root + 1,
in_right);
// 返回当前子树的根节点
return root;
}
};
核心思路总结
这题只需要记住三句话:
1. 前序找根
因为:
前序:根 -> 左 -> 右
所以:
preorder[pre_left]
就是当前根节点。
2. 中序分左右
因为:
中序:左 -> 根 -> 右
找到根的位置:
in_root = index[root]
那么:
in_root左边 = 左子树
in_root右边 = 右子树
3. 递归构造
当前节点:
root = new TreeNode(...)
然后相信递归:
root->left = build(...);
root->right = build(...);
返回:
return root;
易错点
① 左子树数量
int left_size = in_root - in_left;
不是:
in_right - in_root
因为这里只求左子树。
② 左子树前序范围
pre_left + 1
为什么?
因为:
preorder:
根 左 左 左
↑
已经被拿走
所以跳过根。
③ 右子树前序起点
pre_left + left_size + 1
因为:
跳过:
根节点 + 左子树
④ 递归返回值
一定:
return root;
因为父节点需要拿到:
root->left = 左子树根
root->right = 右子树根
这题和你刚刚学的 flatten 本质一样:
flatten:
相信递归处理左右子树,我修改当前节点。
建树:
相信递归返回左右子树,我连接当前节点。
你现在学二叉树递归,重点就是抓住这个模式。