LeetCode #108 Easy

Convert Sorted Array to Binary Search Tree

Convert Sorted Array to Binary Search Tree: build a height-balanced binary search tree from an array sorted in ascending order.

Constraints
  • 1 <= nums.length <= 10⁴
  • -10⁴ <= nums[i] <= 10⁴
  • nums is sorted in a strictly increasing order
arraydivide and conquertreebinary search tree
Open on LeetCode ↗
Convert Sorted Array to Binary Search Tree diagramA labelled diagram of the structure this problem turns on.the middle element roots the tree; the halves become subtrees-10-30590-105from [-10, -3]from [5, 9]halves differ in size by at most one, so heights do too — balance by construction
02

Intuition

Choosing the middle element as the root splits the remaining values into two halves of nearly equal size, and every value on the left is smaller while every value on the right is larger — which is exactly the BST property. Recursing on each half applies the same reasoning one level down, so the tree is balanced by construction rather than by any rebalancing step.

How to spot this pattern

Divide and conquer on a sorted array shows up whenever the midpoint carries structural meaning — as a root, a pivot, or a boundary. The signal here is that sortedness plus balance are both required, and one choice of split delivers both.

03

Approach

Try it first

Before reading on: explain why the middle element must be the root if the tree is to stay balanced, and what shape results from always picking the first element instead. Then decide whether to pass slices or index bounds.

1

The midpoint choice does two jobs at once

Picking mid as the root satisfies the search-tree ordering for free, because the array is sorted: everything at a lower index is smaller and everything at a higher index is larger. It simultaneously satisfies the balance requirement, because the two remaining subarrays differ in length by at most one. No other choice of root achieves both — selecting the first element, for instance, still yields a valid BST but degenerates into a right-leaning chain of height n.

2

Recursion on index ranges, not slices

Pass lo and hi bounds rather than slicing the array. Slicing copies the subarray at every call, adding O(n log n) total copying work and O(n) extra memory for no gain. With index bounds the recursion touches each element once and the only extra space is the call stack. The base case is lo > hi, an empty range, which returns None — that is what terminates the recursion and creates the leaves' null children.

3

Why the answer is not unique, and what balanced means

With an even-length range there are two valid midpoints, and both produce height-balanced trees, so many correct answers exist — LeetCode accepts any of them. Height-balanced means every node's two subtrees differ in height by at most one. The midpoint split guarantees this inductively: if both halves are balanced and their sizes differ by at most one, their heights differ by at most one too. The resulting height is ⌈log₂(n + 1)⌉, the minimum possible for n nodes. Time is O(n), space O(log n) for the stack.

04

Solution & live demo

1class Solution:
2 def sortedArrayToBST(self, nums):
3 def build(lo, hi):
4 if lo > hi:
5 return None
6 mid = (lo + hi) // 2
7 node = TreeNode(nums[mid])
8 node.left = build(lo, mid - 1)
9 node.right = build(mid + 1, hi)
10 return node
11 
12 return build(0, len(nums) - 1)
05

Common pitfalls

Slicing the array at every call

✗ Wrong
node.left = self.sortedArrayToBST(nums[:mid])
node.right = self.sortedArrayToBST(nums[mid + 1:])
✓ Right
node.left = build(lo, mid - 1)
node.right = build(mid + 1, hi)

Each slice copies its subarray, adding O(n log n) work and O(n) memory overall. Index bounds describe the same ranges without duplicating any data.

Choosing the first element as the root

✗ Wrong
node = TreeNode(nums[lo])
node.right = build(lo + 1, hi)
✓ Right
mid = (lo + hi) // 2
node = TreeNode(nums[mid])

The result is still a valid BST but degenerates into a chain of height n, so lookups become linear. The balance requirement is precisely what the midpoint choice satisfies.

Off-by-one in the recursive bounds

✗ Wrong
node.left = build(lo, mid)
node.right = build(mid, hi)
✓ Right
node.left = build(lo, mid - 1)
node.right = build(mid + 1, hi)

Including mid in either half means the range never shrinks past it, so the same element is inserted forever and the recursion overflows the stack.

06

Edge cases

Empty array

lo > hi immediately, so None is returned as the whole tree.

Single element

Becomes a leaf whose two recursive calls both return None.

Two elements

One becomes the root and the other a single child; height 2 is balanced.

Even-length range

Either midpoint is acceptable; the shape differs but balance holds.

Negative and duplicate-free values

Only the ordering matters, so the actual values are irrelevant to the structure.

07

Complexity

Time
O(n)
Space
O(log n)
Each element becomes exactly one node. Space is the recursion depth, which is the tree's height.