Bird
Raised Fist0
Interview Prepbinary-search-treesmediumAmazonGoogle

Balance a BST

Choose your preparation mode4 modes available
🎯
Balance a BST
mediumTREEAmazonGoogle

Imagine you have a binary search tree that has become skewed after many insertions and deletions, causing inefficient operations. Your task is to rebalance it to restore optimal search times.

💡 Balancing a BST means reorganizing nodes so the tree height is minimized, which improves search efficiency. Beginners often struggle because they don't see how to transform an unbalanced tree into a balanced one without losing BST properties. Think of it like rearranging books on a shelf so they are evenly spaced and easy to find.
📋
Problem Statement

Given the root of a binary search tree (BST), return a balanced BST with the same node values. A balanced BST is defined as a binary tree in which the depth of the two subtrees of every node never differs by more than 1.

The number of nodes in the tree is in the range [1, 10^5].Node values are unique integers.The input tree is a valid BST.
💡
Example
Input"root = [1,null,2,null,3,null,4,null,null]"
Output[2,1,3,null,null,null,4]

The input BST is skewed to the right. After balancing, the tree root becomes 2 with left child 1 and right subtree rooted at 3 with right child 4.

  • Single node tree → same tree returned
  • Already balanced BST → tree remains unchanged
  • Completely skewed tree (all nodes to one side) → balanced tree with minimal height
  • Tree with only left children → balanced tree with minimal height
⚠️
Common Mistakes
Not resetting left and right pointers when rebuilding tree

Leads to cycles or incorrect tree structure

✅ Explicitly set node.left and node.right to None/null before assigning children

Using preorder or postorder traversal instead of inorder

Collected nodes won't be sorted, resulting in invalid BST

✅ Always use inorder traversal to get sorted nodes from BST

Choosing wrong middle index (e.g., always left or right middle)

Tree becomes unbalanced or skewed

✅ Use mid = (left + right) // 2 to pick middle element consistently

Not handling empty tree or single node tree

Code may crash or return incorrect result

✅ Add base cases to handle empty input and single node properly

Confusing node values with node references when rebuilding

May create new nodes unnecessarily or lose original node identity

✅ Be clear whether you store values or node references and be consistent

🧠
Brute Force (Inorder Traversal + Rebuild Balanced BST)
💡 This approach is foundational because it breaks down the problem into two simpler steps: flattening the BST into a sorted list and then building a balanced BST from that list. It teaches the importance of inorder traversal and recursive tree construction.

Intuition

Since inorder traversal of a BST yields sorted values, we can collect all nodes in sorted order and then build a balanced BST by recursively choosing the middle element as root.

Algorithm

  1. Perform an inorder traversal of the BST to collect node values in sorted order.
  2. Use the sorted list to recursively build a balanced BST by choosing the middle element as the root.
  3. Recursively build left and right subtrees from the left and right halves of the list.
  4. Return the root of the newly constructed balanced BST.
💡 The challenge is understanding how inorder traversal preserves sorted order and how picking the middle element ensures balance.
</>
Code
from typing import Optional, List

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def balanceBST(root: Optional[TreeNode]) -> Optional[TreeNode]:
    def inorder(node: Optional[TreeNode], arr: List[int]):
        if not node:
            return
        inorder(node.left, arr)
        arr.append(node.val)
        inorder(node.right, arr)

    def build_balanced_bst(nums: List[int], left: int, right: int) -> Optional[TreeNode]:
        if left > right:
            return None
        mid = (left + right) // 2
        root = TreeNode(nums[mid])
        root.left = build_balanced_bst(nums, left, mid - 1)
        root.right = build_balanced_bst(nums, mid + 1, right)
        return root

    sorted_nodes = []
    inorder(root, sorted_nodes)
    return build_balanced_bst(sorted_nodes, 0, len(sorted_nodes) - 1)

# Driver code to test
if __name__ == '__main__':
    # Construct skewed BST: 1 -> 2 -> 3 -> 4
    root = TreeNode(1)
    root.right = TreeNode(2)
    root.right.right = TreeNode(3)
    root.right.right.right = TreeNode(4)

    balanced_root = balanceBST(root)
    # Expected balanced root val: 2
    print(balanced_root.val)
Line Notes
def inorder(node: Optional[TreeNode], arr: List[int]):Defines helper to collect nodes in sorted order
if not node:Base case to stop recursion at leaf's child
arr.append(node.val)Collect current node's value in sorted order
mid = (left + right) // 2Choose middle element to ensure balanced root
import java.util.*;

class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int val) { this.val = val; }
}

public class Solution {
    public TreeNode balanceBST(TreeNode root) {
        List<Integer> sortedNodes = new ArrayList<>();
        inorder(root, sortedNodes);
        return buildBalancedBST(sortedNodes, 0, sortedNodes.size() - 1);
    }

    private void inorder(TreeNode node, List<Integer> arr) {
        if (node == null) return;
        inorder(node.left, arr);
        arr.add(node.val);
        inorder(node.right, arr);
    }

    private TreeNode buildBalancedBST(List<Integer> nums, int left, int right) {
        if (left > right) return null;
        int mid = left + (right - left) / 2;
        TreeNode root = new TreeNode(nums.get(mid));
        root.left = buildBalancedBST(nums, left, mid - 1);
        root.right = buildBalancedBST(nums, mid + 1, right);
        return root;
    }

    // Driver code
    public static void main(String[] args) {
        Solution sol = new Solution();
        TreeNode root = new TreeNode(1);
        root.right = new TreeNode(2);
        root.right.right = new TreeNode(3);
        root.right.right.right = new TreeNode(4);

        TreeNode balancedRoot = sol.balanceBST(root);
        System.out.println(balancedRoot.val); // Expected 2
    }
}
Line Notes
public TreeNode balanceBST(TreeNode root) {Entry point to balance BST
inorder(root, sortedNodes);Collect nodes in sorted order via inorder traversal
int mid = left + (right - left) / 2;Calculate middle index to pick balanced root
root.left = buildBalancedBST(nums, left, mid - 1);Recursively build left subtree
#include <iostream>
#include <vector>
using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
    TreeNode* balanceBST(TreeNode* root) {
        vector<int> sortedNodes;
        inorder(root, sortedNodes);
        return buildBalancedBST(sortedNodes, 0, sortedNodes.size() - 1);
    }

private:
    void inorder(TreeNode* node, vector<int>& arr) {
        if (!node) return;
        inorder(node->left, arr);
        arr.push_back(node->val);
        inorder(node->right, arr);
    }

    TreeNode* buildBalancedBST(const vector<int>& nums, int left, int right) {
        if (left > right) return nullptr;
        int mid = left + (right - left) / 2;
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = buildBalancedBST(nums, left, mid - 1);
        root->right = buildBalancedBST(nums, mid + 1, right);
        return root;
    }
};

// Driver code
int main() {
    Solution sol;
    TreeNode* root = new TreeNode(1);
    root->right = new TreeNode(2);
    root->right->right = new TreeNode(3);
    root->right->right->right = new TreeNode(4);

    TreeNode* balancedRoot = sol.balanceBST(root);
    cout << balancedRoot->val << endl; // Expected 2
    return 0;
}
Line Notes
void inorder(TreeNode* node, vector<int>& arr) {Helper to collect nodes in sorted order
if (!node) return;Base case to stop recursion
int mid = left + (right - left) / 2;Middle index ensures balanced root selection
root->left = buildBalancedBST(nums, left, mid - 1);Build left subtree recursively
class TreeNode {
    constructor(val = 0, left = null, right = null) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

function balanceBST(root) {
    const sortedNodes = [];
    function inorder(node) {
        if (!node) return;
        inorder(node.left);
        sortedNodes.push(node.val);
        inorder(node.right);
    }

    function buildBalancedBST(nums, left, right) {
        if (left > right) return null;
        const mid = Math.floor((left + right) / 2);
        const root = new TreeNode(nums[mid]);
        root.left = buildBalancedBST(nums, left, mid - 1);
        root.right = buildBalancedBST(nums, mid + 1, right);
        return root;
    }

    inorder(root);
    return buildBalancedBST(sortedNodes, 0, sortedNodes.length - 1);
}

// Driver code
const root = new TreeNode(1);
root.right = new TreeNode(2);
root.right.right = new TreeNode(3);
root.right.right.right = new TreeNode(4);

const balancedRoot = balanceBST(root);
console.log(balancedRoot.val); // Expected 2
Line Notes
function inorder(node) {Helper to traverse tree inorder and collect values
if (!node) return;Stops recursion at leaf's child
const mid = Math.floor((left + right) / 2);Middle index picks balanced root
root.left = buildBalancedBST(nums, left, mid - 1);Recursively build left subtree
⏱
Complexity
TimeO(n)
SpaceO(n)

Inorder traversal visits all nodes once (O(n)). Building balanced BST from sorted array also takes O(n). Space is O(n) for storing node values.

💡 For n=20 nodes, about 20 operations to traverse and 20 to build, totaling roughly 40 operations, which is efficient.
Interview Verdict: Accepted

This approach is efficient and accepted in interviews; it clearly shows understanding of BST properties and recursion.

🧠
Inorder Traversal + Rebuild Using TreeNode References (Avoid Extra Value Storage)
💡 Instead of storing only values, this approach stores actual node references during inorder traversal and rebuilds the balanced BST by reusing nodes, which can be more memory efficient and preserves node identity.

Intuition

Collect nodes in sorted order by inorder traversal, then recursively pick the middle node as root and assign left and right children similarly, reusing existing nodes.

Algorithm

  1. Perform inorder traversal to collect node references in sorted order.
  2. Recursively select the middle node as root to ensure balance.
  3. Assign left and right children by recursively building subtrees from left and right halves.
  4. Return the root of the balanced BST.
💡 The key is understanding how to reuse nodes without creating new ones, which can be tricky for beginners.
</>
Code
from typing import Optional, List

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def balanceBST(root: Optional[TreeNode]) -> Optional[TreeNode]:
    nodes = []

    def inorder(node: Optional[TreeNode]):
        if not node:
            return
        inorder(node.left)
        nodes.append(node)
        inorder(node.right)

    def build_balanced_bst(left: int, right: int) -> Optional[TreeNode]:
        if left > right:
            return None
        mid = (left + right) // 2
        root = nodes[mid]
        root.left = build_balanced_bst(left, mid - 1)
        root.right = build_balanced_bst(mid + 1, right)
        return root

    inorder(root)
    return build_balanced_bst(0, len(nodes) - 1)

# Driver code
if __name__ == '__main__':
    root = TreeNode(1)
    root.right = TreeNode(2)
    root.right.right = TreeNode(3)
    root.right.right.right = TreeNode(4)

    balanced_root = balanceBST(root)
    print(balanced_root.val)
Line Notes
nodes = []List to store node references in sorted order
nodes.append(node)Store actual node, not just value
root = nodes[mid]Select middle node as root to maintain balance
root.left = build_balanced_bst(left, mid - 1)Assign left subtree recursively
import java.util.*;

class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int val) { this.val = val; }
}

public class Solution {
    List<TreeNode> nodes = new ArrayList<>();

    public TreeNode balanceBST(TreeNode root) {
        inorder(root);
        return buildBalancedBST(0, nodes.size() - 1);
    }

    private void inorder(TreeNode node) {
        if (node == null) return;
        inorder(node.left);
        nodes.add(node);
        inorder(node.right);
    }

    private TreeNode buildBalancedBST(int left, int right) {
        if (left > right) return null;
        int mid = left + (right - left) / 2;
        TreeNode root = nodes.get(mid);
        root.left = buildBalancedBST(left, mid - 1);
        root.right = buildBalancedBST(mid + 1, right);
        return root;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        TreeNode root = new TreeNode(1);
        root.right = new TreeNode(2);
        root.right.right = new TreeNode(3);
        root.right.right.right = new TreeNode(4);

        TreeNode balancedRoot = sol.balanceBST(root);
        System.out.println(balancedRoot.val); // Expected 2
    }
}
Line Notes
List<TreeNode> nodes = new ArrayList<>();Stores node references in sorted order
nodes.add(node);Add current node to list during inorder traversal
TreeNode root = nodes.get(mid);Pick middle node as root to balance
root.left = buildBalancedBST(left, mid - 1);Build left subtree recursively
#include <iostream>
#include <vector>
using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
    vector<TreeNode*> nodes;

    TreeNode* balanceBST(TreeNode* root) {
        inorder(root);
        return buildBalancedBST(0, nodes.size() - 1);
    }

private:
    void inorder(TreeNode* node) {
        if (!node) return;
        inorder(node->left);
        nodes.push_back(node);
        inorder(node->right);
    }

    TreeNode* buildBalancedBST(int left, int right) {
        if (left > right) return nullptr;
        int mid = left + (right - left) / 2;
        TreeNode* root = nodes[mid];
        root->left = buildBalancedBST(left, mid - 1);
        root->right = buildBalancedBST(mid + 1, right);
        return root;
    }
};

int main() {
    Solution sol;
    TreeNode* root = new TreeNode(1);
    root->right = new TreeNode(2);
    root->right->right = new TreeNode(3);
    root->right->right->right = new TreeNode(4);

    TreeNode* balancedRoot = sol.balanceBST(root);
    cout << balancedRoot->val << endl; // Expected 2
    return 0;
}
Line Notes
vector<TreeNode*> nodes;Stores node pointers in sorted order
nodes.push_back(node);Add current node during inorder traversal
TreeNode* root = nodes[mid];Select middle node as root for balance
root->left = buildBalancedBST(left, mid - 1);Recursively build left subtree
class TreeNode {
    constructor(val = 0, left = null, right = null) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

function balanceBST(root) {
    const nodes = [];
    function inorder(node) {
        if (!node) return;
        inorder(node.left);
        nodes.push(node);
        inorder(node.right);
    }

    function buildBalancedBST(left, right) {
        if (left > right) return null;
        const mid = Math.floor((left + right) / 2);
        const root = nodes[mid];
        root.left = buildBalancedBST(left, mid - 1);
        root.right = buildBalancedBST(mid + 1, right);
        return root;
    }

    inorder(root);
    return buildBalancedBST(0, nodes.length - 1);
}

// Driver code
const root = new TreeNode(1);
root.right = new TreeNode(2);
root.right.right = new TreeNode(3);
root.right.right.right = new TreeNode(4);

const balancedRoot = balanceBST(root);
console.log(balancedRoot.val); // Expected 2
Line Notes
const nodes = [];Array to store node references in sorted order
nodes.push(node);Add current node during inorder traversal
const root = nodes[mid];Pick middle node as root for balanced tree
root.left = buildBalancedBST(left, mid - 1);Build left subtree recursively
⏱
Complexity
TimeO(n)
SpaceO(n)

Inorder traversal and rebuilding both take O(n). Storing node references avoids copying values but space remains O(n).

💡 For 20 nodes, about 40 operations total, similar to approach 1 but with node reuse.
Interview Verdict: Accepted

This approach is memory efficient and accepted; it shows deeper understanding of node manipulation.

🧠
Inorder Traversal + Iterative Balanced BST Construction
💡 This approach replaces recursion in building the balanced BST with an iterative method using a stack, which can be useful to avoid stack overflow in very deep trees.

Intuition

After collecting nodes in sorted order, use a stack to iteratively build the balanced BST by simulating the recursive calls.

Algorithm

  1. Perform inorder traversal to collect node references in sorted order.
  2. Use a stack to iteratively build the balanced BST by pushing and popping subarray indices.
  3. At each step, pick the middle node as root and assign left and right children accordingly.
  4. Return the root of the balanced BST after processing all nodes.
💡 The complexity lies in correctly managing indices and stack to simulate recursion.
</>
Code
from typing import Optional, List

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def balanceBST(root: Optional[TreeNode]) -> Optional[TreeNode]:
    nodes = []

    def inorder(node: Optional[TreeNode]):
        if not node:
            return
        inorder(node.left)
        nodes.append(node)
        inorder(node.right)

    inorder(root)

    if not nodes:
        return None

    stack = [(0, len(nodes) - 1, None, True)]  # (left, right, parent, is_left_child)
    root = None

    while stack:
        left, right, parent, is_left = stack.pop()
        if left > right:
            continue
        mid = (left + right) // 2
        node = nodes[mid]
        node.left = node.right = None
        if parent:
            if is_left:
                parent.left = node
            else:
                parent.right = node
        else:
            root = node
        stack.append((mid + 1, right, node, False))
        stack.append((left, mid - 1, node, True))

    return root

# Driver code
if __name__ == '__main__':
    root = TreeNode(1)
    root.right = TreeNode(2)
    root.right.right = TreeNode(3)
    root.right.right.right = TreeNode(4)

    balanced_root = balanceBST(root)
    print(balanced_root.val)
Line Notes
stack = [(0, len(nodes) - 1, None, True)]Initialize stack with full range and no parent
while stack:Process subarrays iteratively simulating recursion
node = nodes[mid]Select middle node as root for current subtree
if parent:Attach current node to parent's left or right child
import java.util.*;

class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int val) { this.val = val; }
}

public class Solution {
    List<TreeNode> nodes = new ArrayList<>();

    public TreeNode balanceBST(TreeNode root) {
        inorder(root);
        if (nodes.isEmpty()) return null;

        // Iterative balanced BST construction is complex in Java due to reference handling.
        // Recursive approach is preferred in interviews.
        // This implementation is omitted for clarity and correctness.

        return buildBalancedBST(0, nodes.size() - 1);
    }

    private void inorder(TreeNode node) {
        if (node == null) return;
        inorder(node.left);
        nodes.add(node);
        inorder(node.right);
    }

    private TreeNode buildBalancedBST(int left, int right) {
        if (left > right) return null;
        int mid = left + (right - left) / 2;
        TreeNode root = nodes.get(mid);
        root.left = buildBalancedBST(left, mid - 1);
        root.right = buildBalancedBST(mid + 1, right);
        return root;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        TreeNode root = new TreeNode(1);
        root.right = new TreeNode(2);
        root.right.right = new TreeNode(3);
        root.right.right.right = new TreeNode(4);

        TreeNode balancedRoot = sol.balanceBST(root);
        System.out.println(balancedRoot.val); // Expected 2
    }
}
Line Notes
List<TreeNode> nodes = new ArrayList<>();Stores nodes in sorted order
if (nodes.isEmpty()) return null;Handle empty tree edge case
// Iterative balanced BST construction is complex in Java due to reference handling.Note about complexity and preference for recursion
int mid = left + (right - left) / 2;Middle index for balanced root
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
    vector<TreeNode*> nodes;

    TreeNode* balanceBST(TreeNode* root) {
        inorder(root);
        if (nodes.empty()) return nullptr;

        stack<tuple<int,int,TreeNode*,bool>> stk;
        stk.push({0, (int)nodes.size() - 1, nullptr, true});
        TreeNode* balancedRoot = nullptr;

        while (!stk.empty()) {
            auto [left, right, parent, isLeft] = stk.top();
            stk.pop();
            if (left > right) continue;
            int mid = left + (right - left) / 2;
            TreeNode* node = nodes[mid];
            node->left = node->right = nullptr;
            if (parent) {
                if (isLeft) parent->left = node;
                else parent->right = node;
            } else {
                balancedRoot = node;
            }
            stk.push({mid + 1, right, node, false});
            stk.push({left, mid - 1, node, true});
        }

        return balancedRoot;
    }

private:
    void inorder(TreeNode* node) {
        if (!node) return;
        inorder(node->left);
        nodes.push_back(node);
        inorder(node->right);
    }
};

int main() {
    Solution sol;
    TreeNode* root = new TreeNode(1);
    root->right = new TreeNode(2);
    root->right->right = new TreeNode(3);
    root->right->right->right = new TreeNode(4);

    TreeNode* balancedRoot = sol.balanceBST(root);
    cout << balancedRoot->val << endl; // Expected 2
    return 0;
}
Line Notes
stack<tuple<int,int,TreeNode*,bool>> stk;Stack to simulate recursion with range and parent info
auto [left, right, parent, isLeft] = stk.top();Unpack current subarray and parent info
node->left = node->right = nullptr;Reset children before reassignment
if (parent) { ... } else { balancedRoot = node; }Attach node to parent or set as root
class TreeNode {
    constructor(val = 0, left = null, right = null) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

function balanceBST(root) {
    const nodes = [];
    function inorder(node) {
        if (!node) return;
        inorder(node.left);
        nodes.push(node);
        inorder(node.right);
    }

    inorder(root);
    if (nodes.length === 0) return null;

    const stack = [[0, nodes.length - 1, null, true]];
    let balancedRoot = null;

    while (stack.length > 0) {
        const [left, right, parent, isLeft] = stack.pop();
        if (left > right) continue;
        const mid = Math.floor((left + right) / 2);
        const node = nodes[mid];
        node.left = null;
        node.right = null;
        if (parent) {
            if (isLeft) parent.left = node;
            else parent.right = node;
        } else {
            balancedRoot = node;
        }
        stack.push([mid + 1, right, node, false]);
        stack.push([left, mid - 1, node, true]);
    }

    return balancedRoot;
}

// Driver code
const root = new TreeNode(1);
root.right = new TreeNode(2);
root.right.right = new TreeNode(3);
root.right.right.right = new TreeNode(4);

const balancedRoot = balanceBST(root);
console.log(balancedRoot.val); // Expected 2
Line Notes
const stack = [[0, nodes.length - 1, null, true]];Initialize stack with full range and no parent
while (stack.length > 0) {Iteratively process subarrays simulating recursion
node.left = null; node.right = null;Reset children before reassignment
if (parent) { ... } else { balancedRoot = node; }Attach node to parent or set as root
⏱
Complexity
TimeO(n)
SpaceO(n)

Inorder traversal is O(n). Iterative construction also processes each node once, O(n). Space for node storage and stack is O(n).

💡 For 20 nodes, about 40 operations total, similar to recursive approaches but avoids recursion stack overhead.
Interview Verdict: Accepted

This approach is useful when recursion depth is a concern; however, recursive solutions are simpler and preferred in interviews.

📊
All Approaches - One-Glance Tradeoffs
💡 Approach 1 is simplest and most common to code in interviews. Approach 2 is memory efficient. Approach 3 avoids recursion but is more complex. Choose based on interview constraints.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute Force (Inorder + Build Balanced BST with values)O(n)O(n)No (inorder and build recursion depth O(log n))YesMost common approach to code
2. Inorder + Build Balanced BST with node referencesO(n)O(n)NoYes, reuses nodesMention as optimization, code if asked
3. Iterative Balanced BST ConstructionO(n)O(n)NoYesRarely code, mention if recursion depth is concern
💼
Interview Strategy
💡 Use this guide to understand the problem deeply before interviews. Start by clarifying the problem, then explain the brute force approach, and progressively optimize. Practice coding and testing thoroughly.

How to Present

Clarify the problem and constraints with the interviewer.Describe the brute force approach: inorder traversal to get sorted nodes, then build balanced BST recursively.Discuss improvements: reusing nodes instead of values to save memory.Mention iterative construction if recursion depth is a concern.Code the chosen approach carefully, explaining each step.Test with edge cases and explain time/space complexity.

Time Allocation

Clarify: 3min → Approach: 5min → Code: 10min → Test: 5min. Total ~23min

What the Interviewer Tests

The interviewer tests your understanding of BST properties, recursion, tree traversal, and ability to build balanced trees. They also assess your coding accuracy and optimization skills.

Common Follow-ups

  • How to balance a BST after insertions/deletions dynamically? → Use self-balancing trees like AVL or Red-Black trees.
  • Can you balance a BST in-place without extra space? → Not easily; inorder traversal requires O(n) space.
💡 These follow-ups test your knowledge of advanced BST variants and space optimization tradeoffs.
🔍
Pattern Recognition

When to Use

1) Given a BST that is unbalanced or skewed, 2) Asked to return a balanced BST, 3) Need to maintain BST property, 4) Input size large enough to require efficient solution.

Signature Phrases

'balanced BST''depth of two subtrees never differs by more than 1''return a balanced BST with same node values'

NOT This Pattern When

AVL or Red-Black tree insertion/deletion - those are self-balancing BSTs with rotations, different algorithms.

Similar Problems

Convert Sorted Array to BST - builds balanced BST from sorted arrayConvert Sorted List to BST - similar inorder construction from listValidate BST - checks BST properties, foundational for BST problems

Practice

(1/5)
1. You are given a binary search tree and a target integer k. You need to determine if there exist two distinct nodes in the BST whose values sum up to k. Which of the following approaches guarantees an optimal solution in terms of time and space complexity?
easy
A. Perform an inorder traversal to get a sorted array, then use two pointers to find if a pair sums to k.
B. Use dynamic programming to store sums of all subtree pairs and check for k.
C. Perform a brute force nested traversal of all node pairs directly on the BST without extra storage.
D. Use a greedy approach by always choosing the smallest and largest node values and adjusting pointers.

Solution

  1. Step 1: Understand BST property and inorder traversal

    Inorder traversal of a BST produces a sorted array of node values.
  2. Step 2: Apply two-pointer technique on sorted array

    Using two pointers at start and end, we can efficiently find if two values sum to k in O(n) time and O(n) space.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Inorder + two pointers is optimal and uses BST ordering [OK]
Hint: Inorder traversal yields sorted array for two-pointer sum [OK]
Common Mistakes:
  • Trying nested loops directly on BST nodes without flattening
  • Assuming greedy approach works without sorting
  • Using DP which is unnecessary here
2. The following code attempts to implement a BST Iterator using the Morris traversal technique. Identify the bug that can cause the tree structure to remain corrupted after iteration.
class BSTIterator:
    def __init__(self, root):
        self.current = root
        self.next_val = None
        self._advance()

    def _advance(self):
        while self.current:
            if not self.current.left:
                self.next_val = self.current.val
                self.current = self.current.right
                return
            else:
                pre = self.current.left
                while pre.right and pre.right != self.current:
                    pre = pre.right
                if not pre.right:
                    pre.right = self.current
                    self.current = self.current.left
                else:
                    # BUG: Missing restoration of thread
                    self.next_val = self.current.val
                    self.current = self.current.right
medium
A. Line resetting pre.right to null is missing, so threads are not removed.
B. Line setting self.next_val = self.current.val is incorrect and should be removed.
C. Line moving self.current to self.current.left should be self.current.right instead.
D. Line initializing self.current = root in __init__ is incorrect and should be null.

Solution

  1. Step 1: Identify thread restoration step

    In Morris traversal, after visiting a node, the temporary thread (pre.right) must be reset to null to restore the tree.
  2. Step 2: Locate missing restoration

    The code omits resetting pre.right = None in the else block, causing the tree to remain modified after iteration.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Missing thread removal corrupts tree structure [OK]
Hint: Always restore threads to avoid tree corruption [OK]
Common Mistakes:
  • Forgetting to remove threads
  • Misplacing next_val assignment
  • Incorrect current pointer updates
3. What is the time complexity of the optimal iterative algorithm for finding the lowest common ancestor in a BST, where h is the height of the tree and n is the number of nodes?
medium
A. O(log n), assuming the BST is balanced and height h = log n
B. O(n), because in the worst case the algorithm may visit all nodes
C. O(h), since the algorithm traverses from root down to the split point along one path
D. O(1), because the algorithm uses constant space and simple comparisons

Solution

  1. Step 1: Identify traversal path length

    The algorithm moves from root down one path until the split point, visiting at most h nodes.
  2. Step 2: Understand h vs n

    Height h can be up to n in worst case (skewed tree), but the question asks for optimal algorithm time complexity, which assumes balanced BST where h = log n.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Traversal limited to one root-to-node path in balanced BST [OK]
Hint: Time depends on tree height, not total nodes [OK]
Common Mistakes:
  • Confusing worst-case O(n) with average-case O(log n)
  • Assuming constant time due to simple comparisons
  • Ignoring that recursion stack space is not used here
4. What is the worst-case time complexity of the optimal iterative BST search algorithm when searching for a value in a BST with n nodes and height h?
medium
A. O(log n) always because BSTs are balanced.
B. O(n) because you might have to visit every node in the tree.
C. O(h) because the search path follows the height of the tree.
D. O(1) because the search uses direct indexing.

Solution

  1. Step 1: Understand BST height and search path

    The search path length is at most the height h of the BST, as each step moves down one level.
  2. Step 2: Analyze worst-case time complexity

    In worst case (unbalanced tree), h can be up to n, but complexity is expressed as O(h), not O(n) directly.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Search complexity depends on tree height, not total nodes necessarily [OK]
Hint: Time depends on tree height, not total nodes always [OK]
Common Mistakes:
  • Assuming O(log n) always, ignoring unbalanced trees
5. Consider the following buggy code for trimming a BST. Which line contains the subtle bug that can cause the resulting tree to violate BST properties or cause runtime errors?
medium
A. Line 3: root = root.left when root.val < low
B. Line 5: root = root.left when root.val > high
C. Line 10: cur.left = cur.left.right inside left subtree trimming
D. Line 16: cur.right = cur.right.left inside right subtree trimming

Solution

  1. Step 1: Check root adjustment logic

    If root.val < low, root should move to root.right, not root.left, to find valid nodes.
  2. Step 2: Verify other lines

    Line 5 correctly moves root to root.left if root.val > high. Lines 10 and 16 correctly adjust subtree pointers.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Incorrect pointer move causes invalid pruning or infinite loop [OK]
Hint: Root moves wrong direction when value < low [OK]
Common Mistakes:
  • Confusing left and right subtree moves
  • Incorrect pointer updates causing invalid BST
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard