Convert Sorted Array to Balanced BST in DSA Rust - Time & Space Complexity
We want to know how the time needed to build a balanced binary search tree changes as the input array grows.
How does the number of steps grow when we convert a sorted array into a balanced tree?
Analyze the time complexity of the following code snippet.
fn sorted_array_to_bst(nums: &[i32]) -> Option> {
if nums.is_empty() {
return None;
}
let mid = nums.len() / 2;
Some(Box::new(TreeNode {
val: nums[mid],
left: sorted_array_to_bst(&nums[..mid]),
right: sorted_array_to_bst(&nums[mid + 1..]),
}))
}
This code builds a balanced binary search tree by choosing the middle element as root and recursively doing the same for left and right halves.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls splitting the array and creating tree nodes.
- How many times: Each element is visited once to create a node, but the array is split repeatedly.
Each recursive call splits the array roughly in half, and each element is processed once to create a node.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 node creations and splits |
| 100 | About 100 node creations and splits |
| 1000 | About 1000 node creations and splits |
Pattern observation: The number of operations grows roughly in direct proportion to the input size.
Time Complexity: O(n)
This means the time to build the tree grows linearly with the number of elements in the array.
[X] Wrong: "Because the function calls itself twice, the time complexity is O(2^n)."
[OK] Correct: Each element is processed only once, and the array size halves each time, so the total work adds up to linear, not exponential.
Understanding this helps you explain how recursion and divide-and-conquer work together efficiently to build balanced trees.
"What if we did not pick the middle element but always picked the first element as root? How would the time complexity change?"
Practice
Solution
Step 1: Understand the problem goal
The goal is to build a BST that is balanced, meaning the tree height is minimized for fast search operations.Step 2: Why minimal height matters
A balanced BST ensures operations like search, insert, and delete run in O(log n) time, which is efficient.Final Answer:
To create a BST with minimal height for efficient search -> Option DQuick Check:
Balanced BST = minimal height = efficient search [OK]
- Thinking BST should reverse array order
- Confusing BST with linked list
- Ignoring balance and height importance
Solution
Step 1: Analyze input parameter type
The function should accept a slice reference &[i32] to avoid ownership issues and allow read-only access.Step 2: Analyze return type
Since the BST node can be empty, returning Option is correct to represent possible None or Some node.Final Answer:
fn sorted_array_to_bst(nums: &[i32]) -> Option -> Option AQuick Check:
Slice input + Option boxed node = correct signature [OK]
- Using Vec instead of slice for input
- Returning TreeNode directly without Option
- Using mutable slice when not needed
fn sorted_array_to_bst(nums: &[i32]) -> Option {
if nums.is_empty() { return None; }
let mid = nums.len() / 2;
Some(Box::new(TreeNode {
val: nums[mid],
left: sorted_array_to_bst(&nums[..mid]),
right: sorted_array_to_bst(&nums[mid+1..]),
}))
}
let nums = [1, 2, 3, 4, 5];
let root = sorted_array_to_bst(&nums);Solution
Step 1: Calculate middle index
Array length is 5, mid = 5 / 2 = 2 (integer division).Step 2: Identify root value
Root node value is nums[2] = 3.Final Answer:
3 -> Option CQuick Check:
Middle element = root = 3 [OK]
- Using mid = (len-1)/2 causing off-by-one
- Choosing first or last element as root
- Confusing left and right subtree indices
fn sorted_array_to_bst(nums: &[i32]) -> Option {
if nums.len() == 0 {
return None;
}
let mid = nums.len() / 2;
Some(Box::new(TreeNode {
val: nums[mid],
left: sorted_array_to_bst(&nums[mid..]),
right: sorted_array_to_bst(&nums[mid+1..]),
}))
}Solution
Step 1: Check left subtree slice
Left subtree should use nums[..mid] to exclude mid element, but code uses nums[mid..], including mid again.Step 2: Consequence of bug
This causes infinite recursion or duplicate nodes because mid element repeats in left subtree.Final Answer:
Left subtree uses nums[mid..] instead of nums[..mid] -> Option AQuick Check:
Left slice must exclude mid [OK]
- Including mid element in left subtree slice
- Ignoring base case condition
- Miscomputing mid index
Solution
Step 1: Understand BST property with duplicates
BST requires left subtree values < node value, right subtree values >= node value to handle duplicates consistently.Step 2: Consistent placement of duplicates
Placing duplicates always in the right subtree maintains order and balance without ambiguity.Final Answer:
Place duplicates consistently in the right subtree during recursion -> Option BQuick Check:
Duplicates in right subtree = BST property maintained [OK]
- Skipping duplicates losing data
- Random placement breaking BST rules
- Putting duplicates only in left subtree
Ready to prove it?
Pass the test and this pattern moves unknown
