前序遍历:[3, 9, 20, 15, 7]
中序遍历:[9, 3, 15, 20, 7]
- 前序遍历知道根节点3, 由中序遍历3的下标为1,知道根节点3左子树只有一个节点9,下标为1后面的所有数在根节点3的右子树上
- 同样右子树第一个节点为20…以此类推构造二叉树
一般化这个过程:
前序遍历先找到根节点,根据中序遍历,找到左子树有几个节点,并且在前序遍历里以后加上相同数量的个数,找到前序遍历最后的下标,在中序遍历对应下标的查找,可以用哈希表快速查找
假设 k 为在中序遍历上找到的前序遍历根节点的下标
突破点在先由中序遍历的k值确定中序遍历的左右区间,再根据其左区间长度,确定前序遍历的区间。
这里在确定一个新节点左右区间的边界时,有几个已知条件是显而易见的,根节点是pl确定的,
- 下一个左子树前序遍历的左边界为: pl + 1, 右子树右边界为:pr;
- 下一个左子树中序遍历的左边界为 : il, 右边界为 : k - 1, 右子树左边界为 k + 1,右边界为 ir,
- 根据中序遍历左区间的长度 得到下一个左子树区间长度为 : k - il, 所以下一个左子树前序遍历的右边界为 pl + 1 + k - il - 1 = pl + k - il, 右子树的左边界在这里加一就好了: pl + k - il + 1.
中序遍历左子树区间[il, k - 1], 右子树区间 [k + 1, ir]
前序遍历的左子树区间长度应与上面的左子树长度相等,为 k - 1 - il + 1 = k - il
所以前序遍历左子树区间为[pl + 1, pl + k - il], 右子树区间为 [pl + k - il + 1, pr]
构建完毕,当pl > pr 时说明没有节点,返回NULL;
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
unordered_map<int,int> hash_map;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
for (int i = 0; i < inorder.size(); i++) hash_map[inorder[i]] = i;
return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() -1);
}
TreeNode* build (vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir) {
if (pl > pr) return NULL;
auto root = new TreeNode(preorder[pl]);
int k = hash_map[preorder[pl]];
root->left = build (preorder, inorder, pl + 1,pl + k - il, il, k - 1);
root->right = build (preorder, inorder, pl + k - il + 1, pr ,k + 1, ir);
return root;
}
};