进入来看我这篇文章的小伙伴应该大都是遇见了leetcode中的105. 从前序与中序遍历序列构造二叉树或是106. 从中序与后序遍历序列构造二叉树,其实这两个题是明显的同意思路,如果一个题目没有思路,可以看看别人的解答,然后把另外一个当作练习,这也是极好的。
原理讲解
这两个题目的思路其实是和之前遍历二叉树的思路是一样的:分而治之。
在这样一个题目中,第一个重要的思路是前序(后序)在vector中的第一个值(最后一个值)是对应的一个父节点。
以下给出leetcode中的例子来帮助说明:
不得不提的是这个题目有这样一个假设:你可以假设树中没有重复的元素。所以以下我会用数字代指节点。
- 在刚拿到这个题目时,我们可以根据前序vector得出判断:3是一个父节点(如果无法理解这一步的话,我们需要复习一下先序遍历)。
- 根据1的结论,我们再由中序队列得出3这个节点的左边是[9],右边是[15,20,7](如果无法理解这一步的话,我们需要复习一下中序遍历)。
- 由2的结论,我们能够知道3这个节点左边的节点有1个,右边有3个。由此得到在前序的情况下:3这个节点左边是[9],右边是[20,15,7]。
=> 到这里我们得到了:
前序:[9] 前序:[20,15,7]
中序:[9] 中序: [15,20,7]
又回到了题目类似的条件。再如此循环,直到为空便可得出结果。
106. 从中序与后序遍历序列构造二叉树:源码
/**
* 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>& inorder, vector<int>& postorder) {
if(!inorder.size()) return nullptr;
root = new TreeNode(NULL);
_computeNode(inorder, postorder, root);
return root;
}
private:
void _computeNode(vector<int> inorder, vector<int> postorder, TreeNode* node) {
if(inorder.size() == 0) {
return;
} else if (inorder.size() == 1) {
node->val = inorder.back();
return ;
} else {
int cur = postorder.back();
node->val = cur;
auto iter = find(inorder.begin(), inorder.end(), cur);
if(iter == inorder.end()) return ;
int rsize = inorder.end() - iter - 1;
int lsize = iter - inorder.begin();
vector<int> lv = vector<int>(inorder.begin(), iter);
vector<int> rv =vector<int>(iter + 1, inorder.end());
if(lv.size()) {
node->left = new TreeNode(NULL);
_computeNode(lv, vector<int>(postorder.begin(), postorder.begin() + lsize), node->left);
}
if(rv.size()) {
node->right = new TreeNode(NULL);
_computeNode(rv, vector<int>(postorder.end() - 1 - rsize, postorder.end() - 1), node->right);
}
}
}
TreeNode* root;
};
——————
105. 从前序与中序遍历序列构造二叉树:源码
/**
* 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.size())
return nullptr;
root = new TreeNode(NULL);
_buildTree(preorder.begin(), preorder.end(), inorder.begin(), inorder.end(), root);
return root;
}
private:
TreeNode* root;
void _buildTree(vector<int>::iterator preBegin, vector<int>::iterator preEnd,
vector<int>::iterator inBegin, vector<int>::iterator inEnd, TreeNode* node)
{
if(preEnd - preBegin == 0) return ;
else if(preEnd - preBegin == 1)
{
node->val = *preBegin;
return ;
}else {
int cur = *preBegin;
node->val = cur;
auto iter = find(inBegin, inEnd, cur);
if(iter == inEnd) return ;
int lsize = iter - inBegin;
int rsize = inEnd - iter - 1;
if(lsize)
{
node->left = new TreeNode(NULL);
_buildTree(preBegin + 1, preBegin + lsize + 1, inBegin, iter, node->left);
}
if(rsize) {
node->right = new TreeNode(NULL);
_buildTree(preBegin + lsize + 1, preEnd, iter + 1, inEnd, node->right);
}
}
}
};
总结
在106这个题目中,我用的是vector拷贝复制,性能消耗很大,但贵在思路看着更加清晰,跑起来88ms,比一些python的解决方案还要慢。在105这个题目中,我改正了这个错误,改用迭代器,28ms左右。可见拷贝这一行为的性能消耗之大,谨记。