108. Convert Sorted Array to Binary Search Tree - Explanation
Description
Given a strictly increasing integer array nums, construct a height-balanced binary search tree containing each array value exactly once, and return its root.
A binary search tree has smaller values in every left subtree and larger values in every right subtree. A tree is height-balanced when, at every node, the heights of its left and right subtrees differ by at most one.
There may be several valid answers; return any of them. Example outputs use level-order traversal, with null for missing children and trailing nulls omitted.
Example 1:
Input: nums = [-2,0,4]
Output: [0,-2,4]Explanation: Root 0 has left child -2 and right child 4. The values satisfy the search-tree ordering, and the two subtrees have equal height.
Example 2:
Input: nums = [2,6]
Output: [6,2]Explanation: Root 6 with left child 2 is valid. Root 2 with right child 6 would also be accepted: both trees contain the same values and are height-balanced.
Example 3:
Input: nums = [-5]
Output: [-5]Explanation: The single value becomes the root. A one-node tree is both a binary search tree and height-balanced.
Constraints:
1 <= nums.length <= 10^4-10^4 <= nums[i] <= 10^4numsis strictly increasing.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Binary Search Trees (BST) - Understanding BST properties where left subtree values are less than root and right subtree values are greater
- Recursion - Building solutions by breaking problems into smaller subproblems
- Divide and Conquer - Splitting an array at the middle element to create balanced subtrees
1. Depth First Search
Intuition
To create a height-balanced BST from a sorted array, we need to ensure that for every node, the left and right subtrees have roughly equal heights. Since the array is sorted, the middle element should become the root. All elements before the middle go to the left subtree, and all elements after go to the right subtree. Applying this recursively builds a balanced tree.
Algorithm
- Base case: If the array is empty, return
null. - Find the middle index of the current array segment.
- Create a new tree node with the middle element as its value.
- Recursively build the left subtree using the left half of the array (elements before the middle).
- Recursively build the right subtree using the right half of the array (elements after the middle).
- Return the root node.
# 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 sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None
mid = len(nums) // 2
root = TreeNode(nums[mid])
root.left = self.sortedArrayToBST(nums[:mid])
root.right = self.sortedArrayToBST(nums[mid + 1:])
return rootTime & Space Complexity
- Time complexity:
- Space complexity:
2. Depth First Search (Optimal)
Intuition
The previous approach creates new array copies for each recursive call, which is inefficient. Instead, we can pass indices (left and right boundaries) to indicate the current segment of the array. This avoids array slicing and reduces both time and space overhead while maintaining the same logic of choosing the middle element as root.
Algorithm
- Define a helper function that takes
leftandrightboundary indices. - Base case: If
left > right, returnnull(empty segment). - Calculate the middle index as
(left + right) / 2. - Create a tree node with the value at the middle index.
- Recursively build the left subtree with boundaries
(left, mid - 1). - Recursively build the right subtree with boundaries
(mid + 1, right). - Call the helper with initial boundaries
(0, n - 1)and return the result.
# 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 sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def helper(l, r):
if l > r:
return None
m = (l + r) // 2
root = TreeNode(nums[m])
root.left = helper(l, m - 1)
root.right = helper(m + 1, r)
return root
return helper(0, len(nums) - 1)Time & Space Complexity
- Time complexity:
- Space complexity:
- space for recursion stack.
- space for the output.
3. Iterative DFS
Intuition
The recursive approach can be converted to an iterative one using an explicit stack. We store pending work items on the stack, where each item contains a node to be filled and the array bounds it should use. This simulates the recursive call stack and processes each subtree systematically without recursion.
Algorithm
- If the array is empty, return
null. - Create a root node with a placeholder value.
- Push the root node along with its bounds
(0, n-1)onto a stack. - While the stack is not empty:
- Pop an item containing the node and its bounds
(l, r). - Calculate the middle index and set the node's value to
nums[mid]. - If there are elements to the left (
l <= mid - 1), create a left child and push it with bounds(l, mid - 1). - If there are elements to the right (
mid + 1 <= r), create a right child and push it with bounds(mid + 1, r).
- Pop an item containing the node and its bounds
- Return the root.
# 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 sortedArrayToBST(self, nums: List[int]) -> TreeNode:
if not nums:
return None
root = TreeNode(0)
stack = [(root, 0, len(nums) - 1)]
while stack:
node, l, r = stack.pop()
m = (l + r) // 2
node.val = nums[m]
if l <= m - 1:
node.left = TreeNode(0)
stack.append((node.left, l, m - 1))
if m + 1 <= r:
node.right = TreeNode(0)
stack.append((node.right, m + 1, r))
return rootTime & Space Complexity
- Time complexity:
- Space complexity:
- space for the stack.
- space for the output.
Common Pitfalls
Off-by-One Errors in Subarray Bounds
When recursively building subtrees, a common mistake is including the middle element in one of the subarrays, leading to infinite recursion or duplicate nodes.
# Wrong - includes mid in left subtree
root.left = helper(l, m) # should be m - 1
# Correct
root.left = helper(l, m - 1)
root.right = helper(m + 1, r)Choosing Wrong Middle Element in Even-Length Arrays
For arrays with even length, the middle can be either (l + r) // 2 or (l + r + 1) // 2. While both produce valid height-balanced BSTs, inconsistency can cause confusion. The problem accepts either choice, but stick with one consistently.
Sign in to join the discussion