Topics › Binary Search Trees › Practice Problems
Construct BST from given keys
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given the keys of a binary search tree as an integer array sorted in strictly increasing order. Build a height-balanced BST containing exactly these keys and return its root. Height-balanced means that, for every node, the heights of its left and right subtrees differ by at most one.
This is the problem the SDE sheet links under this title (LeetCode 108, “Convert Sorted Array to Binary Search Tree”). Several trees can be correct for the same input; the methods here always choose the left-middle key as the root of each range, which is one accepted answer. If your keys arrive unsorted, sort them first and then apply the same construction.
Constraints (LeetCode 108): 1 ≤ n ≤ 10^4, values in [-10^4, 10^4], strictly increasing. The Java file also handles an empty array, extreme int values, and 100000 keys.
Example 1
keys = [-10, -3, 0, 5, 9]
answer: 0
/ \
-10 5
\ \
-3 9
0 is the middle key. The keys smaller than it, [-10, -3], form the left subtree, and [5, 9] the right subtree, each built the same way. [0, -3, 9, -10, null, 5] (right-middle choices) would also be accepted.
Example 2
keys = [1, 3]
answer: 1 with right child 3 // 3 with left child 1 is equally valid
Why not just insert the keys one by one? Standard BST insertion of a sorted sequence puts every new key to the right of all previous ones, producing a chain of height n. It is a valid BST, but searching it costs O(n), which defeats the purpose of a search tree. Balance has to come from choosing the root carefully.
Approach 1: Middle key as root, recursing on copied halves
Intuition. For a tree to be balanced, the root must split the keys into two halves of (almost) equal size, so the root should be the median. Because the array is sorted, the median is simply the middle element, everything before it is smaller (it belongs in the left subtree), and everything after it is larger (the right subtree). Each half is again a sorted array, so the same rule builds each subtree.
Why is the result balanced? The two halves have sizes that differ by at most one. A tree built this way from m keys has height floor(log2 m) + 1, and that number does not decrease as m grows, so two halves whose sizes differ by one have heights that differ by at most one. Applying this at every node gives the height-balance property, and the overall height is the minimum possible for n nodes.
The most literal implementation passes the two halves as new arrays.
Algorithm.
- If the array is empty, return
null. - Let
mid = (length - 1) / 2(the left-middle index) and makekeys[mid]the root. - Build the left child from a copy of
keys[0..mid-1]and the right child from a copy ofkeys[mid+1..].
static TreeNode bruteForce(int[] keys) {
if (keys.length == 0) return null;
int mid = (keys.length - 1) / 2; // left-middle for even lengths
TreeNode root = new TreeNode(keys[mid]);
root.left = bruteForce(Arrays.copyOfRange(keys, 0, mid));
root.right = bruteForce(Arrays.copyOfRange(keys, mid + 1, keys.length));
return root;
}
Complexity. Time O(n log n): on each level of the recursion the copied pieces add up to about n elements, and there are about log2 n levels. Space O(n) extra: the copies alive at one time are the arrays along one root-to-leaf path, of sizes about n/2, n/4, ..., which sum to less than n. The n tree nodes themselves are the output.
Approach 2: Optimal, recurse on index bounds
Intuition. The copies carry no information that two indices cannot carry. Describe each subarray by its bounds lo and hi in the original array and pick the middle index inside them. The tree produced is exactly the same as in Approach 1 (the tests check this), but each call now does constant work.
Algorithm.
build(lo, hi): iflo > hi, returnnull.mid = lo + (hi - lo) / 2; create the root fromkeys[mid].root.left = build(lo, mid - 1),root.right = build(mid + 1, hi).- Start with
build(0, n - 1).
static TreeNode optimal(int[] keys) {
return build(keys, 0, keys.length - 1);
}
static TreeNode build(int[] keys, int lo, int hi) {
if (lo > hi) return null;
int mid = lo + (hi - lo) / 2; // same left-middle choice as above
TreeNode root = new TreeNode(keys[mid]);
root.left = build(keys, lo, mid - 1); // keys smaller than keys[mid]
root.right = build(keys, mid + 1, hi); // keys larger than keys[mid]
return root;
}
Dry run on Example 1:
| call | mid |
node created | children calls |
|---|---|---|---|
| build(0, 4) | 2 | 0 | build(0, 1), build(3, 4) |
| build(0, 1) | 0 | -10 | build(0, -1) = null, build(1, 1) |
| build(1, 1) | 1 | -3 | both empty |
| build(3, 4) | 3 | 5 | build(3, 2) = null, build(4, 4) |
| build(4, 4) | 4 | 9 | both empty |
Complexity. Time O(n): one call per key plus one per empty range, each constant work. Space O(log n) for the recursion, because the tree being built is balanced and the recursion depth equals its height.
Edge cases and pitfalls
- Empty array. Both methods return
null;build(0, -1)hits the base case at once. - Even lengths. There are two middle elements. Either choice is correct, but be consistent within one method. Both methods here take the left-middle:
(length - 1) / 2in Approach 1 equalslo + (hi - lo) / 2in Approach 2. - Midpoint overflow.
(lo + hi) / 2can overflowintfor very large indices;lo + (hi - lo) / 2cannot. Withintvalues near the limits the keys themselves are never added, so the extreme-value test passes either way, but the safe midpoint is a good habit. - Unsorted or duplicate keys. The method assumes strictly increasing input. Unsorted keys must be sorted first (
O(n log n)). With duplicate keys the strict BST property (left < node < right) cannot hold, so a convention such as “equal keys go right” must be agreed on first. - Verifying the result. The tests check three things for every input: the inorder traversal equals the input, the height-balance condition holds at every node, and the height is the minimum
floor(log2 n) + 1. They also check that both methods build exactly the expected tree.
Related problems
- Insert a given node in BST is the one-key-at-a-time construction that degrades to a chain on sorted input.
- Construct a BST from a preorder traversal rebuilds a specific BST instead of a balanced one.
- Check for balanced binary tree is the height-balance check used in the tests.
- Check if a tree is a BST or not verifies the ordering property.
- Merge 2 BST’s can end with this exact construction on the merged sorted keys.
- Search in BST is the operation that a balanced tree makes fast.