输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
限制:
0 <= 节点个数 <= 5000
题源链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof
方法一:递归
二叉树的前序遍历顺序是:根节点、左子树、右子树,每个子树的遍历顺序同样满足前序遍历顺序。
二叉树的中序遍历顺序是:左子树、根节点、右子树,每个子树的遍历顺序同样满足中序遍历顺序。
前序遍历的第一个节点是根节点,只要找到根节点在中序遍历中的位置,在根节点之前被访问的节点都位于左子树,在根节点之后被访问的节点都位于右子树,由此可知左子树和右子树分别有多少个节点。
由于树中的节点数量与遍历方式无关,通过中序遍历得知左子树和右子树的节点数量之后,可以根据节点数量得到前序遍历中的左子树和右子树的分界,因此可以进一步得到左子树和右子树各自的前序遍历和中序遍历,可以通过递归的方式,重建左子树和右子树,然后重建整个二叉树。
使用一个 Map 存储中序遍历的每个元素及其对应的下标,目的是为了快速获得一个元素在中序遍历中的位置。调用递归方法,对于前序遍历和中序遍历,下标范围都是从 0 到 n-1,其中 n 是二叉树节点个数。
递归方法的基准情形有两个:判断前序遍历的下标范围的开始和结束,若开始大于结束,则当前的二叉树中没有节点,返回空值 null。若开始等于结束,则当前的二叉树中恰好有一个节点,根据节点值创建该节点作为根节点并返回。
若开始小于结束,则当前的二叉树中有多个节点。在中序遍历中得到根节点的位置,从而得到左子树和右子树各自的下标范围和节点数量,知道节点数量后,在前序遍历中即可得到左子树和右子树各自的下标范围,然后递归重建左子树和右子树,并将左右子树的根节点分别作为当前根节点的左右子节点。
/**
* 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:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty() || inorder.empty()){
return NULL;
}
unordered_map<int, int> indexMap;
int length = preorder.size();
for(int i=0; i<length; i++){
// 存储inorder每个元素的index
indexMap[inorder[i]] = i;
}
TreeNode *root = buildTree(preorder, 0, length-1, inorder, 0, length-1, indexMap);
return root;
}
private:
TreeNode* buildTree(vector<int> preorder, int preorderStart, int preorderEnd, vector<int> inorder, int inorderStart, int inorderEnd, unordered_map<int, int> indexMap){
if (preorderStart > preorderEnd){
return NULL;
}
int rootVal = preorder[preorderStart];
TreeNode *root = new TreeNode(rootVal);
if (preorderStart == preorderEnd){
// 树只有一个元素
return root;
}
else{
// 寻找根节点在中序遍历的位置
int rootIndex = indexMap[rootVal];
int leftNodes = rootIndex - inorderStart, rightNodes = inorderEnd - rootIndex;
TreeNode *leftSubtree = buildTree(preorder, preorderStart + 1, preorderStart + leftNodes, inorder, inorderStart, rootIndex - 1, indexMap);
TreeNode *rightSubtree = buildTree(preorder, preorderEnd - rightNodes + 1, preorderEnd, inorder, rootIndex + 1, inorderEnd, indexMap);
root->left = leftSubtree;
root->right = rightSubtree;
return root;
}
}
};
/**
* 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 {
private:
unordered_map<int, int> index;
public:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder,
int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return nullptr;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = index[preorder[preorder_root]];
// 先把根节点建立出来
TreeNode* root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点(递归过程中,当前节点就是其左右子树的根节点)
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素
// 就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left = myBuildTree(preorder, inorder,
preorder_left + 1, preorder_left + size_left_subtree,
inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」
// 的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right = myBuildTree(preorder, inorder,
preorder_left + size_left_subtree + 1, preorder_right,
inorder_root + 1, inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 构造哈希映射,帮助我们快速定位根节点
for (int i = 0; i < n; ++i) {
index[inorder[i]] = i;
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 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 {
private:
unordered_map<int, int> index;
public:
TreeNode* selfBuildTree(vector<int> &preorder, vector<int> &inorder,
int preorder_left, int preorder_right, int inorder_left, int inorder_right){
/*preorder: tree的前序遍历,inorder: tree的中序遍历,
preorder_left: 当前树所有节点按前序遍历第一个节点在preorder vector中的索引,
preorder_right: 当前树所有节点按前序遍历最后一个节点在preorder vector中的索引,
preorder_left: 当前树所有节点按中序遍历第一个节点在inorder vector中的索引,
preorder_right: 当前树所有节点按中序遍历最后一个节点在preorder vector中的索引*/
if (preorder_left > preorder_right) {
// 当前节点不在有子树
return NULL;
}
// 获取当前树根节点在前序遍历中的索引(preorder中的所有元素都是自身的根节点)
// 当前树根节点在前序遍历中的索引就是此时树所有节点的头节点在preorder vector的索引
int preorder_root = preorder_left;
// 查找此时树的根节点在中序遍历vector的索引
//(中序遍历:左子树所有节点全在根节点左侧, 右子树所有节点钱在根节点的右侧)
int inorder_root = index[preorder[preorder_left]];
TreeNode *root = new TreeNode(preorder[preorder_left]);
// 计算当前树,左子树的数目
//(中序遍历:左子树所有节点全在根节点左侧, 右子树所有节点钱在根节点的右侧)
int size_left_subtree = inorder_root - inorder_left;
// (递归过程中,当前节点就是其左右子树的根节点)
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素
// 先序遍历中此时根节点的左子树第一个节点索引是此时树除去第一个根节点之后那个节点索引。
// 就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left = selfBuildTree(preorder, inorder,
preorder_left+1, preorder_left+size_left_subtree,
inorder_left, inorder_root-1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」
// 的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right = selfBuildTree(preorder, inorder,
preorder_left+1+size_left_subtree, preorder_right,
inorder_root+1, inorder_left);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 构造哈希映射,帮助我们快速定位根节点
for (int i = 0; i < n; ++i) {
index[inorder[i]] = i;
}
// return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
return selfBuildTree(preorder, inorder, 0, n-1, 0, n-1);
}
};