题目描述
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例1:
题目分析:
一般来说涉及二叉树的遍历问题的话,我们应该首先都可以想到利用递归的方法来切入,因为二叉树就其本身而言就是一个递归性质的树,我们可以首先抛开编码,设想自己在画树的时候如何构建一个给出前中序遍历的树。
首先我们来回顾一下二叉树的遍历方法
不难发现常规的二叉树遍历都是左子树位于右子树之前(注意非常规的情况)。分析上面的遍历性质,很容易可以得到,对于前序遍历来说,第一个元素必然是树的根节点,而对于一个树的中序遍历来说,其根节点必然中序遍历序列分为左右两个部分,分别对应左子树和右子树。在此基础上所有的子树也是遵照这个性质的我们知道了这一点性质就可以用来解题了。我们总结出以下递归方法
- 首先在长度为
n
前序序列中找到当前树的根节点,再找到根节点在中序序列中的位置i; 此时中序序列中的0~i-1
就是左子树,i ~n
就是右子树 - 我们可以继续按照性质分别构造左右子树,也就是在前序序列中分别找到左右子树的
root
节点,然后继续在中序序列中划分左右子树的左右子树。(我们需要考虑子树为空的情况) - 构造完左右子树后,输出树的根节点
拿上面的示例分析,我们首先确定前序序列中的第一个元素3为树的根节点,此时在中序序列中找到3的位置并按照其将序列划分为左右两部分分别为左右子树
此时再构造左子树,左子树只有一个节点,其自己为自己的根节点;
继续构造右子树,查询前序序列发现20为右子树的根节点,再在中序序列中查询20,并按照这个节点将右子树划分为左右两部分,分别为15和7,至此树构造完成。
在解题中对于中序遍历中的根节点位置求法我首先使用的是使用find
函数库函数来进行查找,但是find函数的返回值是一个迭代器指针,使用的话还要继续再求迭代器距离,较为繁琐也容易出错,参考官方解答使用一个hashmap
首先对于每个节点的位置进行存储,这样就可以牺牲一些空间来达到代码的简洁性,时间上来说可能也更高效。另外只要使用到指针我们必然要避免指针溢出的问题,尤其是指针参与到循环当中,必然要格外注意这个问题。在实际解题过程中,我们必然会碰到左子树或者右子树为空的情况,此时我们就要理清指针溢出的思路。假如是一个子树为空,我们可以得到其preorderLeft
必然是大于preorderRight
的,所以在每次递归前我们首先要对此做出判定,否则会因指针指向而出错。
题目解答
/**
* 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 {
private:
unordered_map<int, int> index;
public:
TreeNode* inBuildTree(vector<int> preorder,vector<int>inorder,int preorderLeft,int preorderRight,int inorderLeft,int inorderRight){
if(preorderLeft>preorderRight){
return nullptr;
}
int preorderRoot = preorderLeft;
int inorderRoot = index[preorder[preorderRoot]];
//构造树的根节点
TreeNode* root = new TreeNode(preorder[preorderRoot]);
//求出左子树大小然后构造左子树
int leftSize = inorderRoot-inorderLeft;
root->left=inBuildTree(preorder,inorder,preorderLeft+1,preorderLeft+leftSize,inorderLeft,inorderRoot-1);
//构造右子树
root->right = inBuildTree(preorder,inorder,preorderLeft+leftSize+1,preorderRight,inorderRoot+1,inorderRight);
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 inBuildTree(preorder,inorder,0,n-1,0,n-1);
}
};
总结:总体来说递归的方法思路是比较容易想到的,但是在具体实施中确实会出现很多问题。这就需要在使用循环递归时注意好边界条件