https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
解法一、递归
前序遍历的序列组成形式为:根, {左子树前序结果}, {右子树前序结果}
中序遍历的序列组成形式为:{左子树中序结果}, 根, {右子树中序结果}
把上面这两行写出来,递归解法就很容易想到了。
很明显,我们需要知道左子树的节点个数,才好获取到左子树的前序结果。由于树中无重复元素,我们可以利用中序遍历的结果,获取根的下标,这样也就间接得到了左子树中序结果的数组长度,同时也达到了我们的目的。
如果在递归过程中每次都遍历中序结果去找根的下标,会耗费太多时间。注意到,中序结果是不会变的,所以完全可以利用一个哈希表来存储 值 → \rightarrow →下标 的映射。
代码
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
unordered_map<int, int> m;
for (int i = 0; i < inorder.size(); ++i) m[inorder[i]] = i;
return helper(m, preorder, 0, preorder.size() - 1, 0, preorder.size() - 1);
}
private:
TreeNode* helper(unordered_map<int, int>& m, vector<int>& preorder, int l1, int r1, int l2, int r2) {
if (l1 > r1) return nullptr;
TreeNode* root = new TreeNode(preorder[l1]);
int index = m[preorder[l1]], left_num = index - l2;
root->left = helper(m, preorder, l1 + 1, l1 + left_num, l2, index - 1);
root->right = helper(m, preorder, l1 + left_num + 1, r1, index + 1, r2);
return root;
}
};
复杂度分析
前序结果和中序结果都需要遍历一次,时间 O ( n ) O(n) O(n),哈希表获取元素时间 O ( 1 ) O(1) O(1),所以总的时间复杂度为 O ( n ) O(n) O(n)。除了构造树所需的 O ( n ) O(n) O(n)空间外,还需要 O ( n ) O(n) O(n)的空间来存储哈希表, O ( h ) O(h) O(h)的空间来存储递归栈( h < n h < n h<n),所以总的空间复杂度 O ( n ) O(n) O(n)。
解法二、迭代
迭代的方法比递归的要复杂很多。
首先,我们思考前序遍历中任意两个连续节点x和y的关系,只有两种情况:
- x有左儿子时,y是x的左儿子
- x没有左儿子时,y是x或者x的某个祖先的右儿子(并且x在这个祖先的左子树中)
我们用一个栈来维护当前节点的所有这样的祖先:当前节点在祖先的左子树中,栈顶节点就是当前节点。用一个index来指向中序遍历的序列。
初始时,根节点自然为preorder[0],我们从1开始遍历preorder。index = 0指向inorder[0]。根节点入栈。
栈顶节点为x,遍历到的值为y。
- 如果x不等于inorder[index],这说明x是有左儿子的,导致中序遍历先打印了x的左儿子的值,否则中序遍历就会直接打印x的值,与前序的结果相同。
此时y是x的左儿子 - 如果x等于inorder[index],这说明x没有左儿子,如何判断y是哪个节点的右儿子呢?注意,当所有节点都只有左儿子时,前序遍历的结果与中序遍历的结果相反。这种情况下我们依次弹出栈顶的值,就会与inorder[index++]的值对应。如果在这个过程中又出现了值不相等的情况,那么就说明中序遍历输出了一个右儿子,此时最后弹出栈的那个节点就是我们要找的祖先。(从这个祖先到x的路径上的所有节点都没有右儿子)
此时y是那个祖先的右儿子 - 不论怎样,都要把y入栈(对于第二种情况,尽管y是其父亲的右儿子,但其父亲已经出栈了,保证了栈内节点的意义)
代码
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
stack<TreeNode*> s;
int n = preorder.size();
if (!n) return nullptr;
int index1 = 0, index2 = 0;
TreeNode* root = new TreeNode(preorder[index1++]);
s.push(root);
while (index1 < n) {
TreeNode* node = s.top();
TreeNode* tmp = new TreeNode(preorder[index1++]);
if (node->val == inorder[index2]) {
while (!s.empty() && s.top()->val == inorder[index2]) {
node = s.top();
s.pop();
index2++;
}
node->right = tmp;
}
else node->left = tmp;
s.push(tmp);
}
return root;
}
};
复杂度分析
时间复杂度和空间复杂度都是 O ( n ) O(n) O(n)。
本文介绍了如何使用递归和迭代两种方法解决LeetCode第105题,即根据给定的前序和中序遍历构建二叉树。解法一利用哈希表提高查找效率,递归构建树,时间复杂度和空间复杂度均为O(n)。解法二通过栈和指针跟踪中序遍历,同样达到O(n)的时间和空间复杂度。

402

被折叠的 条评论
为什么被折叠?



