
Problem Statement
Given two integer arrays preorder
and inorder
, you are required to reconstruct a binary tree. These arrays represent the preorder and inorder traversals of a binary tree, respectively. The preorder
array provides the root-first traversal sequence of the tree, while the inorder
array offers a left-root-right traversal sequence. Utilizing these two sequences, you are to build and return the binary tree structure that conforms to these traversal orders.
Examples
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]
Constraints
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder
andinorder
consist of unique values.- Each value of
inorder
also appears inpreorder
. preorder
is guaranteed to be the preorder traversal of the tree.inorder
is guaranteed to be the inorder traversal of the tree.
Approach and Intuition
When tackling the problem of reconstructing a binary tree from its preorder and inorder traversal data, it’s crucial to understand the basic properties of these traversals:
- Preorder Traversal:
- The first element is always the root of the tree.
- Following the root, the sequence contains nodes from the left subtree, followed by nodes from the right subtree.
- Inorder Traversal:
- Nodes from the left subtree appear first, followed by the root, and then nodes from the right subtree.
Using the above properties, we can infer the following steps to reconstruct the tree:
- The first element in the preorder list identifies the root node of the binary tree.
- Locate this root within the inorder list. The position of this root splits the inorder list into left and right subtrees.
- Subsequently, use the length of the left subtree determined from the inorder list to split the elements in the preorder list into left and right subtrees.
- Recursively apply the above steps to construct the left subtree from the left segments of the preorder and inorder lists.
- Similarly, use the right segments of the lists to recursively construct the right subtree.
- Proceed with this division and recursive construction until all elements from both arrays are utilized, which completes the tree construction.
The recursion leverages the first element extraction in the preorder array to identify root nodes and uses the root’s index in the inorder array to delineate bounds of left and right subtrees. Since both arrays contain unique values and each value from one also appears in the other, this method reliably constructs the correct binary tree structure.
Solutions
- C++
- Java
- C
- JavaScript
- Python
class Solution {
public:
int currentPreorderIndex = 0;
unordered_map<int, int> indexMap;
TreeNode* buildSubTree(vector<int>& preorder, int start, int end) {
if (start > end) return nullptr;
int rootVal = preorder[currentPreorderIndex++];
TreeNode* root = new TreeNode(rootVal);
root->left = buildSubTree(preorder, start, indexMap[rootVal] - 1);
root->right = buildSubTree(preorder, indexMap[rootVal] + 1, end);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
currentPreorderIndex = 0;
for (int i = 0; i < inorder.size(); i++) {
indexMap[inorder[i]] = i;
}
return buildSubTree(preorder, 0, preorder.size() - 1);
}
};
Explore the C++ solution that efficiently constructs a binary tree from preorder and inorder traversal arrays. The key components of this solution involve utilizing recursion and hash mapping to maintain track of indices, which significantly optimizes the search process within the inorder array.
Solution Breakdown:
Define the class Solution with private members:
currentPreorderIndex
to keep track of the index for the currently being processed node in the preorder array.indexMap
to store the index of each value in the inorder array for quick access.
Implement the
buildSubTree
method:- This method is responsible for creating the tree recursively. It receives the preorder array and the current subarray bounds (
start
andend
) as parameters. - If the start index exceeds the end, return
nullptr
indicating no subtree is formed. - Extract the root value from the preorder array using
currentPreorderIndex
and increment it. - Create a new tree node
TreeNode
with the root value. - Recursively construct the left subtree using indices from
start
toindexMap[rootVal] - 1
. - Similarly, construct the right subtree using indices from
indexMap[rootVal] + 1
toend
. - Return the constructed
TreeNode
.
- This method is responsible for creating the tree recursively. It receives the preorder array and the current subarray bounds (
Implement the
buildTree
method:- Initialize
currentPreorderIndex
to 0. - Populate
indexMap
where each key-value pair consists of a value from the inorder array and its corresponding index. - Start the recursive construction of the tree by calling
buildSubTree
with bounds from 0 topreorder.size() - 1
.
- Initialize
This approach, leveraging a hash map and recursion, allows for swift and efficient reconstruction of the binary tree without searching the entire inorder array repeatedly for the root element, which makes it time-efficient. This technique greatly reduces the time complexity typically encountered in tree construction problems.
class Solution {
int preorderPosition;
Map<Integer, Integer> idxMapInorder;
public TreeNode constructBinaryTree(int[] preOrder, int[] inOrder) {
preorderPosition = 0;
// Create a map from value to its index in inorder
idxMapInorder = new HashMap<>();
for (int i = 0; i < inOrder.length; i++) {
idxMapInorder.put(inOrder[i], i);
}
return buildTreeFromPreIn(preOrder, 0, preOrder.length - 1);
}
private TreeNode buildTreeFromPreIn(int[] preOrder, int start, int end) {
// Base condition if no elements are present
if (start > end) return null;
// Root is the current element at preorderPosition in preOrder
int rootVal = preOrder[preorderPosition++];
TreeNode rootNode = new TreeNode(rootVal);
// Construct left and right subtree excluding the root's index
rootNode.left = buildTreeFromPreIn(
preOrder,
start,
idxMapInorder.get(rootVal) - 1
);
rootNode.right = buildTreeFromPreIn(
preOrder,
idxMapInorder.get(rootVal) + 1,
end
);
return rootNode;
}
}
The provided Java code implements a solution to construct a binary tree from a given preorder and inorder traversal array. This algorithm involves:
- Initializing a global index to keep track of the current position within the preorder array.
- Creating a hashmap to map each value in the inorder array to its index. This aids in quick look-up for the root nodes in recursive calls.
- Implement the
constructBinaryTree
method which initializes the necessary data structures and calls a helper method to construct the tree recursively. - Implementing the
buildTreeFromPreIn
private method where the actual binary tree is built. In this method:- It checks the base case where the start index exceeds the end index, implying that the current subtree is empty.
- It determines the root node of the current subtree using the current preorder position.
- Recursively constructs the left subtree by considering the element range before the root element in the inorder traversal.
- Recursively constructs the right subtree by considering the element range after the root element in the inorder traversal.
The use of hashmap for index tracking optimizes the time complexity, as finding the root's index in the inorder array becomes constant time. This solution is efficient and handles the construction of a binary tree from traversal data effectively.
struct KeyValueMapping {
int key;
int value;
UT_hash_handle hh;
};
struct TreeNode* preorderToInorderTree(int* preorderArray, int* inorderArray,
struct KeyValueMapping** indexMapping,
int* preorderIdx, int leftBoundary, int rightBoundary) {
if (leftBoundary > rightBoundary) return NULL;
int rootNodeValue = preorderArray[(*preorderIdx)++];
struct TreeNode* newRoot = (struct TreeNode*)malloc(sizeof(struct TreeNode));
newRoot->val = rootNodeValue;
struct KeyValueMapping* found;
HASH_FIND_INT(*indexMapping, &rootNodeValue, found);
newRoot->left = preorderToInorderTree(preorderArray, inorderArray, indexMapping, preorderIdx, leftBoundary,
found->value - 1);
newRoot->right = preorderToInorderTree(preorderArray, inorderArray, indexMapping, preorderIdx,
found->value + 1, rightBoundary);
return newRoot;
}
struct TreeNode* constructBinaryTree(int* preorder, int preorderLength, int* inorder,
int inorderLength) {
struct KeyValueMapping* indexMapping = NULL;
for (int idx = 0; idx < inorderLength; idx++) {
struct KeyValueMapping* mapEntry =
(struct KeyValueMapping*)malloc(sizeof(struct KeyValueMapping));
mapEntry->key = inorder[idx];
mapEntry->value = idx;
HASH_ADD_INT(indexMapping, key, mapEntry);
}
int rootIndex = 0;
return preorderToInorderTree(preorder, inorder, &indexMapping, &rootIndex, 0,
inorderLength - 1);
}
The provided C code defines a solution for constructing a binary tree from preorder and inorder traversal arrays. Here's a concise summary of how this process is implemented:
Data Structures Used:
- The
KeyValueMapping
struct is designed to map the integer keys from the inorder array to their corresponding indices. - The
TreeNode
struct is a node of the tree containing the integer value and pointers to left and right child nodes.
- The
Utility Functions and Approach:
constructBinaryTree
initializes a hash map usingUT_hash_handle
to record the indices of elements in the inorder array for quick lookup. This function sets up the initial conditions and callspreorderToInorderTree
.preorderToInorderTree
performs the recursive construction of the binary tree. It uses the preorder array to determine the root node and the index mapping from the inorder array to partition the tree into left and right subtrees.
Recursive Construction:
- Using the next element in the preorder array as the root, the function locates this root in the inorder array using the hash map.
- It then constructs the entire tree by recursively building the left subtree from the elements before the root in the inorder array and the right subtree from the elements after the root.
- Recursion ends when the boundaries for the left and right children are invalid, implying that a leaf node is reached.
Memory Management:
malloc
is used for dynamic memory allocation for each tree node and mapping entry, essential for building the tree and mapping structure during execution.
This code is effective for building binary trees when both preorder and inorder traversal lists are known, leveraging hash tables for efficient index lookups, which significantly speeds up the construction process.
var constructBinaryTree = function(preorderList, inorderList) {
let indexPreorder = 0;
let mapInorderIndex = new Map();
for (let index = 0; index < inorderList.length; index++) {
mapInorderIndex.set(inorderList[index], index);
}
function buildSubTree(start, end) {
if (start > end) return null;
let rootVal = preorderList[indexPreorder++];
let treeNode = new TreeNode(rootVal);
treeNode.left = buildSubTree(start, mapInorderIndex.get(rootVal) - 1);
treeNode.right = buildSubTree(mapInorderIndex.get(rootVal) + 1, end);
return treeNode;
}
return buildSubTree(0, preorderList.length - 1);
};
The provided JavaScript solution efficiently constructs a binary tree from its preorder and inorder traversal sequences. The essential element here is the mapping of inorder values to their indices to expedite the reconstruction process. Build a comprehensive map of element indices from the inorderList
before inititating the recursive construction of the subtree. The buildSubTree
function facilitates the recursive construction using the defined bounds on the current subtree's inorder list, updating the subtree root accordingly each recursive call. A binary tree node is created for each non-null value in the preorderList
by referencing the incremented preorder index. This node then becomes a root, with recursive calls to build its left and right children. In summary, the function builds the binary tree by utilizing the provided preorder list for the root node sequence, and the mapped inorder list to split the tree into left and right subtrees recursively. The strategy ensures an efficient and systematic assembly of the binary tree with respect to its specified traversal orders.
class Solution:
def constructBinaryTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
def construct_subtree(start, end):
nonlocal index_tracker
if start > end:
return None
root_val = preorder[index_tracker]
new_node = TreeNode(root_val)
index_tracker += 1
new_node.left = construct_subtree(start, value_to_index[root_val] - 1)
new_node.right = construct_subtree(value_to_index[root_val] + 1, end)
return new_node
index_tracker = 0
value_to_index = {value: idx for idx, value in enumerate(inorder)}
return construct_subtree(0, len(preorder) - 1)
The Python code provided defines a method to construct a binary tree from given preorder and inorder traversal sequences. The algorithm uses recursive strategy to build the tree and tracks node values through a dictionary that maps each value to its index in the inorder list. Here's how the process generally unfolds:
A nested function
construct_subtree(start, end)
is defined, which handles the recursive construction of the binary tree. It takesstart
andend
as parameters to determine the current segment of the inorder traversal to consider.The global variable
index_tracker
is used to keep track of the current index in the preorder list. This helps in picking the root for each subtree being constructed recursively.If the
start
parameter is greater thanend
, it indicates that the current segment is invalid for a subtree, and thusNone
is returned, indicating the absence of a node.The value of the current root node of the subtree is picked from the preorder list using
index_tracker
, and a new tree nodenew_node
is created with this root value.The recursive calls are made separately for the left and right children of the current root. For the left child, the subtree corresponds to the elements before the root's index in the inorder sequence; for the right child, it follows the root’s index.
A dictionary
value_to_index
is created outside the nested function to quickly find the index of any element from the inorder list, which optimizes the process of splitting the inorder list into left and right parts for the recursive calls.The
construct_subtree
function is initially called with the full range of indices from the preorder list as parameters to start the tree construction.The final tree root is returned after all recursive calls complete, providing a structured binary tree as specified by the preorder and inorder lists.
This solution effectively builds a binary tree with a time complexity primarily determined by the recursive tree construction and an auxiliary space complexity due to the space allocated for the recursion stack and the dictionary mapping values to indices.
No comments yet.