graph TD;
F(6) --- C(3) & E(5)
C ~~~ N1:::hidden
C --- B(2)
B ~~~ N2:::hidden
B --- A(1)
E --- G(0)
E ~~~ N3:::hidden
classDef hidden display:none
1
2
3
4
5
6
7
8
9
10
11
12
Input: nums =[3,2,1,6,0,5]Output: [6,3,5,null,2,0,null,null,1]Explanation: The recursive calls are as follow:- The largest value in[3,2,1,6,0,5]is6. Left prefix is[3,2,1] and right suffix is[0,5].- The largest value in[3,2,1]is3. Left prefix is[] and right suffix is[2,1].- Empty array, so no child.- The largest value in[2,1]is2. Left prefix is[] and right suffix is[1].- Empty array, so no child.- Only one element, so child is a node with value 1.- The largest value in[0,5]is5. Left prefix is[0] and right suffix is[].- Only one element, so child is a node with value 0.- Empty array, so no child.
Example 2:
1
2
3
4
5
6
7
8
9
Tree =3\2\1Input: nums =[3,2,1]Output: [3,null,2,null,1]
classSolution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return buildTree(nums, 0, nums.length);
}
private TreeNode buildTree(int[] nums, int start, int end) {
if (start >= end) {
returnnull;
}
// Find the maximum value and its indexint maxIdx = start;
for (int i = start; i < end; i++) {
if (nums[i]> nums[maxIdx]) {
maxIdx = i;
}
}
// Create the root node TreeNode root =new TreeNode(nums[maxIdx]);
// Recursively build the left and right subtrees root.left= buildTree(nums, start, maxIdx);
root.right= buildTree(nums, maxIdx + 1, end);
return root;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
classSolution:
defconstructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
ifnot nums:
returnNone# Find the maximum value and its index max_val = max(nums)
max_idx = nums.index(max_val)
# Create the root node root = TreeNode(max_val)
# Recursively construct the left and right subtrees root.left = self.constructMaximumBinaryTree(nums[:max_idx])
root.right = self.constructMaximumBinaryTree(nums[max_idx +1:])
return root
⏰ Time complexity: O(n^2). The recursive function finds the maximum element for each subarray, which takes O(n) per level of recursion. In the worst case (when the array is sorted in descending or ascending order), this results in O(n^2).
🧺 Space complexity: O(n). Space is used for the recursive stack, which can go as deep as O(n) in the worst case. Additionally, space is used for storing the tree nodes, i.e., O(n). Hence overall space complexity is O(n).
When constructing the tree, the maximum value is kept as the root. Values smaller than the maximum and to the left are added to the left subtree, while values smaller than the maximum and to the right form the right subtree.
A monotonic decreasing stack can be used for this purpose. With a decreasing stack:
While pushing a number i, during the popping process, the last popped element becomes the left child of i.
If an element remains on the stack after popping, it represents a value larger than i. Hence, i becomes a candidate for this element’s right subtree, though it might not always be the final right child.
For e.g., given nums = [3, 1, 2], first 3 is processed and pushed onto the stack. Next, 1 is pushed, becoming the candidate for node(3).right. When 2 is processed, 1 is popped, making node(2).left = node(1). Then 2 is pushed and assigned as node(3).right. The correct tree structure is achieved.
Traverse the array, creating nodes sequentially and maintaining a decreasing sequence in the stack.
For each new node:
Pop elements from the stack while their value is smaller than that of the current node. The last popped node becomes the left child of the current node (curNode.left = last popped node).
After the popping process, the top of the stack remains larger, making it the root of the current node (peek.right = curNode).
classSolution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
Stack<TreeNode> stack =new Stack<>();
for (int n : nums) {
TreeNode curNode =new TreeNode(n);
// Assign left children via poppingwhile (!stack.isEmpty() && stack.peek().val< curNode.val) {
curNode.left= stack.pop();
}
// Link current node as the right child of the top node in the stackif (!stack.isEmpty()) {
stack.peek().right= curNode;
}
// Push current node onto the stack stack.push(curNode);
}
// Return the node at the bottom of the stack (root of the constructed tree)return stack.get(0);
}
}
classSolution:
defconstructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
stack = []
for n in nums:
cur_node = TreeNode(n)
# Assign left childrenwhile stack and stack[-1].val < cur_node.val:
cur_node.left = stack.pop()
# Link current node to the previous node in stack as its right childif stack:
stack[-1].right = cur_node
# Push the current node onto the stack stack.append(cur_node)
# Return the bottom-most node in the stack (represents the root)return stack[0]
⏰ Time complexity: O(n). Each node is pushed and popped from the stack at most once, resulting in a time complexity of O(n), where n is the size of the array.
🧺 Space complexity: O(n). The stack can hold up to n nodes in the worst case, making the space complexity O(n).