Construct Binary Tree from Preorder and Inorder Traversal medium
Problem Statement
Given two integer arrays preorder
and inorder
where preorder
is the preorder traversal of a binary tree and inorder
is the inorder traversal of the same binary tree, construct and return the binary tree.
Example 1
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] Output: [3,9,20,null,null,15,7]
Example 2
Input: preorder = [-1], inorder = [-1] Output: [-1]
Steps and Explanation
The key to solving this problem lies in understanding the properties of preorder and inorder traversals.
- Preorder traversal: Root -> Left -> Right. The first element in the
preorder
array is always the root of the tree. - Inorder traversal: Left -> Root -> Right. The inorder traversal helps us identify the left and right subtrees.
The algorithm recursively builds the tree:
- Find the root: The first element of
preorder
is the root. - Find the root in inorder: Locate the root's position in the
inorder
array. This splits theinorder
array into left and right subtrees. - Recursively build left and right subtrees:
- The elements to the left of the root in
inorder
form the left subtree's inorder traversal. The corresponding elements inpreorder
(starting from the second element and up to the size of the left subtree) form the left subtree's preorder traversal. Recursively call the function to build the left subtree. - Similarly, the elements to the right of the root in
inorder
form the right subtree's inorder traversal. The remaining elements inpreorder
form the right subtree's preorder traversal. Recursively call the function to build the right subtree.
- The elements to the left of the root in
- Connect the subtrees: Connect the left and right subtrees to the root node.
Code
#include <vector>
#include <unordered_map>
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:
TreeNode* buildTree(std::vector<int>& preorder, std::vector<int>& inorder) {
if (preorder.empty()) return nullptr;
//Using a hashmap for faster inorder lookup (optional optimization)
std::unordered_map<int, int> inorder_map;
for (int i = 0; i < inorder.size(); ++i) {
inorder_map[inorder[i]] = i;
}
return buildTreeHelper(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1, inorder_map);
}
private:
TreeNode* buildTreeHelper(std::vector<int>& preorder, int preStart, int preEnd,
std::vector<int>& inorder, int inStart, int inEnd,
std::unordered_map<int, int>& inorder_map) {
if (preStart > preEnd) return nullptr;
int rootVal = preorder[preStart];
TreeNode* root = new TreeNode(rootVal);
//Find root in inorder using hashmap for O(1) lookup
int rootIndex = inorder_map[rootVal];
//Size of left subtree
int leftSize = rootIndex - inStart;
//Recursively build left and right subtrees
root->left = buildTreeHelper(preorder, preStart + 1, preStart + leftSize, inorder, inStart, rootIndex - 1, inorder_map);
root->right = buildTreeHelper(preorder, preStart + leftSize + 1, preEnd, inorder, rootIndex + 1, inEnd, inorder_map);
return root;
}
};
Complexity
-
Time Complexity: O(N), where N is the number of nodes in the tree. We visit each node once during the recursive construction. The use of the
unordered_map
improves the lookup time to O(1) in thebuildTreeHelper
function. Without the hashmap, the time complexity would still be O(NlogN) due to searching in theinorder
array in each recursive call. -
Space Complexity: O(N) in the worst case (skewed tree) due to the recursive call stack. The
unordered_map
also uses O(N) space. In the best case (balanced tree), the space complexity is O(logN).