A clear explanation of constructing a maximum binary tree recursively using divide and conquer.
Problem Restatement
We are given an array of distinct integers called nums.
We must construct a binary tree using the following rules:
- The root is the maximum number in the array.
- The left subtree is built recursively from the elements to the left of the maximum number.
- The right subtree is built recursively from the elements to the right of the maximum number.
Return the root of the constructed tree.
Input and Output
| Item | Meaning |
|---|---|
| Input | An array of distinct integers |
| Output | The root of the constructed maximum binary tree |
| Root rule | The root is the maximum value in the current subarray |
| Left subtree | Built from elements left of the maximum |
| Right subtree | Built from elements right of the maximum |
Example function shape:
def constructMaximumBinaryTree(nums: List[int]) -> Optional[TreeNode]:
...Examples
Consider:
nums = [3, 2, 1, 6, 0, 5]The maximum value is:
6So 6 becomes the root.
The left side is:
[3, 2, 1]The right side is:
[0, 5]Now build both sides recursively.
For the left side:
[3, 2, 1]the maximum is 3.
So 3 becomes the left child of 6.
Its right side is:
[2, 1]The maximum there is 2.
Then 2 gets right child 1.
For the right side:
[0, 5]the maximum is 5.
So 5 becomes the right child of 6.
Its left child becomes 0.
The final tree is:
6
/ \
3 5
\ /
2 0
\
1Another example:
nums = [1]The maximum is 1, so the tree is simply:
1First Thought: Follow the Definition Directly
The problem already describes the construction process.
For every subarray:
- Find the maximum value.
- Create a node with that value.
- Recursively build the left subtree.
- Recursively build the right subtree.
This is a divide-and-conquer problem.
Each recursive call handles a smaller subarray.
Key Insight
The structure of the tree is completely determined by the maximum element.
Every recursive step splits the array into:
| Part | Meaning |
|---|---|
| Elements before the maximum | Left subtree |
| Maximum element | Current root |
| Elements after the maximum | Right subtree |
So the recursive function only needs to know the current range of indices.
Instead of slicing arrays repeatedly, we can pass:
left index
right indexThis avoids creating extra arrays.
Recursive Definition
Suppose the recursive function is:
build(left, right)This function builds the maximum binary tree from:
nums[left:right+1]The steps are:
- If the range is empty, return
None. - Find the index of the maximum value.
- Create a node.
- Recursively build the left subtree.
- Recursively build the right subtree.
- Return the node.
Algorithm
Start with the full array:
build(0, len(nums) - 1)For every recursive call:
- If
left > right, the subarray is empty. - Scan the range to find the maximum value index.
- Create a node with that value.
- Build the left subtree from the left side.
- Build the right subtree from the right side.
- Return the node.
Correctness
The algorithm follows the exact definition of a maximum binary tree.
For any subarray:
- The algorithm finds the maximum value.
- It creates the root node using that value.
- It recursively constructs the left subtree from all elements before the maximum.
- It recursively constructs the right subtree from all elements after the maximum.
These are exactly the rules required by the problem.
The recursion eventually stops because each recursive call uses a strictly smaller subarray. When the subarray becomes empty, the function returns None.
Therefore, every subtree is constructed correctly, and the final tree is the required maximum binary tree.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n^2) worst case | Each recursive call may scan a large subarray |
| Space | O(n) | Recursion stack in the worst case |
The worst case happens when the array is already sorted.
For example:
[1, 2, 3, 4, 5]Each recursive step scans almost the entire remaining array.
Implementation
from typing import List, Optional
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def constructMaximumBinaryTree(
self,
nums: List[int],
) -> Optional[TreeNode]:
def build(left: int, right: int) -> Optional[TreeNode]:
if left > right:
return None
max_index = left
for i in range(left + 1, right + 1):
if nums[i] > nums[max_index]:
max_index = i
root = TreeNode(nums[max_index])
root.left = build(left, max_index - 1)
root.right = build(max_index + 1, right)
return root
return build(0, len(nums) - 1)Code Explanation
The recursive function:
build(left, right)constructs the tree for the subarray:
nums[left:right+1]If the range is invalid:
if left > right:
return Nonethen there are no nodes in this subtree.
We begin by assuming the leftmost value is the maximum:
max_index = leftThen we scan the range:
for i in range(left + 1, right + 1):and update the maximum index whenever we find a larger value:
if nums[i] > nums[max_index]:
max_index = iAfter finding the maximum value, we create the root node:
root = TreeNode(nums[max_index])Then we recursively construct both sides.
The left subtree uses everything before the maximum:
root.left = build(left, max_index - 1)The right subtree uses everything after the maximum:
root.right = build(max_index + 1, right)Finally, we return the constructed subtree root.
Testing
To test tree problems locally, it is useful to serialize the tree.
def serialize(root):
if root is None:
return None
return [
root.val,
serialize(root.left),
serialize(root.right),
]Example tests:
def run_tests():
s = Solution()
root = s.constructMaximumBinaryTree([3, 2, 1, 6, 0, 5])
assert serialize(root) == [
6,
[
3,
None,
[
2,
None,
[
1,
None,
None,
],
],
],
[
5,
[
0,
None,
None,
],
None,
],
]
root = s.constructMaximumBinaryTree([1])
assert serialize(root) == [
1,
None,
None,
]
root = s.constructMaximumBinaryTree([5, 4, 3])
assert serialize(root) == [
5,
None,
[
4,
None,
[
3,
None,
None,
],
],
]
root = s.constructMaximumBinaryTree([1, 2, 3])
assert serialize(root) == [
3,
[
2,
[
1,
None,
None,
],
None,
],
None,
]
print("all tests passed")Test meaning:
| Test | Why |
|---|---|
[3,2,1,6,0,5] | Standard example with both left and right recursion |
[1] | Smallest valid tree |
[5,4,3] | Descending order creates a right-skewed tree |
[1,2,3] | Ascending order creates a left-skewed tree |