剑指Offer 07 重建二叉树
参考学习题解:
剑指 Offer 07. 重建二叉树(分治算法,清晰图解)作者:Krahets
解题思路:
前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。
以题目示例为例:
前序遍历划分 [ 3 | 9 | 20 15 7 ]
中序遍历划分 [ 9 | 3 | 15 20 7 ]
根据以上性质,可得出以下推论:
- 前序遍历的首元素 为 树的根节点 node 的值。
- 在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。
- 根据中序遍历中的左(右)子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ] 。
作者:jyd
链接:https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-di-gui-fa-qin/
来源:力扣(LeetCode)
为了提升效率,本文使用哈希表 dic 存储中序遍历的值与索引的映射,查找操作的时间复杂度为
O(1) ;
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
unordered_map<int,int> map;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
// 将中序序列用哈希表存储,便于查找根节点
for(int i = 0;i < inorder.size();i++)
map[inorder[i]] = i;
// 传入参数:前序,中序,前序序列根节点,中序序列左边界,中序序列右边界
return build(preorder,inorder,0,0,inorder.size()-1);
}
TreeNode* build(vector<int>& preorder, vector<int>& inorder,int pre_root,int in_left,int in_right){
if(in_left > in_right)
return NULL;
TreeNode* root = new TreeNode(preorder[pre_root]);
// 根节点在中序序列中的位置,用于划分左右子树的边界
int in_root = map[preorder[pre_root]];
// 左子树在前序中的根节点位于:pre_root+1,左子树在中序中的边界:[in_left,in_root-1]
root->left = build(preorder,inorder,pre_root+1,in_left,in_root-1);
// 右子树在前序中的根节点位于:根节点+左子树长度+1 = pre_root+in_root-in_left+1
// 右子树在中序中的边界:[in_root+1,in_right]
root->right = build(preorder,inorder,pre_root+in_root-in_left+1,in_root+1,in_right);
return root;
}
};
####Q:右子树在前序中的根节点位于:根节点+左子树长度+1 = pre_root+in_root-in_left+1 这个是怎么算的。 左子树的长度应该是多少
A:
右子树在前序中的根节点下标 = 根节点下标 + 左子树长度 + 1
其中,根节点下标是 pre_root,左子树的长度可以在中序遍历中计算得到,即 in_root - in_left。
所以,右子树在前序遍历中的根节点下标可以计算为:
pre_root + in_root - in_left + 1
左子树的长度应该是 in_root - in_left,因为在中序遍历中,根节点左边的元素都属于左子树,右边的元素都属于右子树。所以,根据根节点在中序遍历中的下标 in_root 和左边界 in_left 的差值,即可得到左子树的长度。