Bird
Raised Fist0
Interview Prepbinary-search-treesmediumAmazonGoogle

Balance a BST

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Initialize nodes list and start inorder traversal

Create an empty list 'nodes' to store nodes in sorted order. Begin inorder traversal from the root node (value 1).

💡 This step sets up the data structure to collect nodes in ascending order, which is key to balancing.
Line:nodes = [] def inorder(node: Optional[TreeNode]): if not node: return
💡 The inorder traversal will visit nodes in ascending order, which is essential for balanced BST construction.
📊
Balance a BST - Watch the Algorithm Execute, Step by Step
Watching the algorithm step-by-step reveals how the skewed BST is transformed into a balanced one by reorganizing nodes based on inorder traversal.
Step 1/20
·Active fillAnswer cell
Current node: 1
1234
DFS Stack
1 (entered)
1234
DFS Stack
1 (left_done)
Current node: 1
1234
DFS Stack
1 (node_added)
Result: [1]
Current node: 2
1234
DFS Stack
2 (entered)
Result: [1]
1234
DFS Stack
2 (left_done)
Result: [1]
Current node: 2
1234
DFS Stack
2 (node_added)
Result: [1, 2]
Current node: 3
1234
DFS Stack
3 (entered)
Result: [1, 2]
1234
DFS Stack
3 (left_done)
Result: [1, 2]
Current node: 3
1234
DFS Stack
3 (node_added)
Result: [1, 2, 3]
Current node: 4
1234
DFS Stack
4 (entered)
Result: [1, 2, 3]
1234
DFS Stack
4 (left_done)
Result: [1, 2, 3]
Current node: 4
1234
Result: [1, 2, 3, 4]
1234
Result: [1, 2, 3, 4]
Current node: 2
1234
Result: [1, 2, 3, 4]
Current node: 2
1234
Result: [1, 2, 3, 4]
Current node: 3
1234
Result: [1, 2, 3, 4]
Current node: 3
1234
Result: [1, 2, 3, 4]
Current node: 4
1234
Result: [1, 2, 3, 4]
1234
Result: [1, 2, 3, 4]
Current node: 2
1234
Result: [1, 2, 3, 4]
Return: 2

Key Takeaways

Inorder traversal collects nodes in sorted order, which is the foundation for balancing the BST.

This insight is hard to see from code alone because the traversal and balancing are separate phases.

Choosing the middle node of the sorted nodes array as root at each step ensures the tree is balanced.

Visualizing the middle selection clarifies how the tree height is minimized.

Using a stack to iteratively build the balanced BST replaces recursion and manages subtree construction explicitly.

Seeing the stack operations helps understand how the algorithm controls subtree boundaries.

Practice

(1/5)
1. Consider the following BSTIterator implementation using Morris traversal:
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:
                    pre.right = None
                    self.next_val = self.current.val
                    self.current = self.current.right
Given the BST with nodes 2 (root), 1 (left child), and 3 (right child), what is the value of next_val after the first call to _advance() during initialization?
easy
A. 3
B. 2
C. 1
D. null

Solution

  1. Step 1: Trace _advance() starting at root=2

    Since root has a left child (1), find predecessor of 2 in left subtree: node 1 has no right child, so set 1.right = 2 and move current to 1.
  2. Step 2: At current=1, no left child, so set next_val=1 and move current to 1.right which points back to 2 (thread).

    Return from _advance() with next_val=1.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    First inorder element is 1, matches next_val after first advance [OK]
Hint: Morris traversal first yields leftmost node [OK]
Common Mistakes:
  • Assuming next_val is root value initially
  • Forgetting thread creation
  • Confusing next_val with current node
2. You are given a binary tree where exactly two nodes have been swapped by mistake, violating the binary search tree property. Which approach guarantees an optimal solution to recover the tree without changing its structure or using extra space beyond a constant amount?
easy
A. Use a bottom-up dynamic programming approach to reconstruct the BST by comparing subtrees and swapping nodes.
B. Perform an inorder traversal, store values in a list, sort the list, then overwrite the tree nodes with sorted values.
C. Apply a greedy approach by swapping any two nodes that violate the BST property during a single inorder traversal pass.
D. Use Morris inorder traversal to detect the two swapped nodes during traversal and swap their values, achieving O(1) space complexity.

Solution

  1. Step 1: Understand the problem constraints

    The problem requires recovering a BST where two nodes are swapped, without extra space and without changing the tree structure.
  2. Step 2: Identify the approach that meets constraints

    Morris inorder traversal allows inorder traversal without recursion or stack, detecting swapped nodes on the fly and swapping their values, achieving O(1) space.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Morris traversal is the only approach with O(1) space and no structural changes [OK]
Hint: Morris traversal enables O(1) space recovery [OK]
Common Mistakes:
  • Assuming sorting values is optimal despite extra space
  • Believing greedy single pass fixes all cases
  • Confusing DP with tree recovery
3. You are given a binary tree where each node has a unique integer value. You need to find whether a given value exists in the tree efficiently by leveraging the tree's ordering properties. Which approach guarantees the fastest search in the worst case?
easy
A. Use the BST property to decide whether to search left or right subtree at each node, recursively or iteratively.
B. Perform a breadth-first search (BFS) to find the value level by level.
C. Traverse the entire tree recursively checking each node until the value is found.
D. Use dynamic programming to store previously searched subtrees to avoid repeated work.

Solution

  1. Step 1: Understand the problem constraints

    The tree is a BST, so left subtree nodes are less than the node, right subtree nodes are greater.
  2. Step 2: Identify the optimal search approach

    Using the BST property allows skipping half of the tree at each step, leading to O(h) time complexity.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    BST property guides search direction efficiently [OK]
Hint: BST property enables directed search, not full traversal [OK]
Common Mistakes:
  • Thinking BFS or full traversal is optimal for BST search
4. Consider the following buggy code for inserting a value into a BST. Which line contains the subtle bug that causes the insertion to fail silently in some cases?
def insertIntoBST(root, val):
    if root is None:
        return TreeNode(val)
    if val < root.val:
        insertIntoBST(root.left, val)
    else:
        insertIntoBST(root.right, val)
    return root
medium
A. Line 2: if root is None
B. Line 4: if val < root.val
C. Line 7: insertIntoBST(root.right, val)
D. Line 5: insertIntoBST(root.left, val)

Solution

  1. Step 1: Analyze recursive calls

    Recursive calls to insertIntoBST on root.left or root.right do not update the parent's child pointer.
  2. Step 2: Identify missing assignment

    Lines 5 and 7 call insertIntoBST but do not assign the returned subtree back to root.left or root.right, so insertion is lost.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Missing assignment causes silent insertion failure [OK]
Hint: Recursive insert must assign returned subtree to parent's child [OK]
Common Mistakes:
  • Not updating parent's child pointer
  • Confusing base case with recursive step
  • Incorrect comparison operator
5. Suppose the BST can contain duplicate values and you want to find the kth smallest element counting duplicates as separate elements. Which modification to the iterative inorder traversal approach correctly handles duplicates without extra space or preprocessing?
hard
A. Perform a full inorder traversal collecting all values, then remove duplicates before indexing kth element.
B. Modify the traversal to skip nodes with duplicate values to avoid counting duplicates multiple times.
C. Use a hash set to track visited values and only increment count for unique values.
D. Keep the traversal unchanged; duplicates are naturally counted separately in inorder traversal order.

Solution

  1. Step 1: Understand duplicates in BST inorder traversal

    Inorder traversal visits nodes in sorted order including duplicates as separate nodes.
  2. Step 2: Check if modification is needed

    Since duplicates appear as separate nodes, counting each node visited naturally counts duplicates separately without extra logic.
  3. Step 3: Evaluate other options

    Skipping duplicates (B) or using a hash set (C) changes semantics. Removing duplicates after full traversal (D) is inefficient and breaks counting duplicates separately.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Inorder traversal inherently counts duplicates separately [OK]
Hint: Duplicates appear as separate nodes in inorder traversal [OK]
Common Mistakes:
  • Trying to skip duplicates
  • Using extra space unnecessarily
  • Assuming duplicates must be merged