Attic
DSATrees

Binary Search Trees

Inorder sorted property, logarithmic search, insertion, 3-case deletion, and BST validation

A Binary Search Tree (BST) is a node-based binary tree data structure with the following ordering invariant:

  • The left subtree of a node contains only keys strictly less than the node's key.
  • The right subtree of a node contains only keys strictly greater than the node's key.
  • Both the left and right subtrees must also be binary search trees.

The Golden Property of BST: An inorder traversal (left -> root -> right) of a Binary Search Tree always visits values in strictly ascending sorted order.


1. Search Operation

Because subtrees are partitioned by value, binary search eliminates half of the remaining nodes at each step:

SearchBSTIterative.java
public TreeNode searchBST(TreeNode root, int val) {
    TreeNode curr = root;

    while (curr != null && curr.val != val) {
        if (val < curr.val) {
            curr = curr.left;
        } else {
            curr = curr.right;
        }
    }

    return curr; // null if not found
}

2. Insert Operation

Insertion always lands at a null leaf position that preserves the BST invariant:

InsertBST.java
public TreeNode insertIntoBST(TreeNode root, int val) {
    if (root == null) return new TreeNode(val);

    if (val < root.val) {
        root.left = insertIntoBST(root.left, val);
    } else if (val > root.val) {
        root.right = insertIntoBST(root.right, val);
    }

    return root;
}

3. Delete Operation (The 3 Classic Cases)

Deleting a node from a BST is one of the most celebrated algorithmic problems because removing an internal node cannot violate subtree ordering.

DeleteNodeBST.java
public TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return null;

    if (key < root.val) {
        root.left = deleteNode(root.left, key);
    } else if (key > root.val) {
        root.right = deleteNode(root.right, key);
    } else {
        // Found node to delete

        // Case 1 & 2: 0 or 1 child
        if (root.left == null) return root.right;
        if (root.right == null) return root.left;

        // Case 3: Node with 2 children
        // Find Inorder Successor (minimum node in right subtree)
        TreeNode successor = getMin(root.right);

        // Copy successor's value to current node
        root.val = successor.val;

        // Recursively delete the successor from the right subtree
        root.right = deleteNode(root.right, successor.val);
    }

    return root;
}

private TreeNode getMin(TreeNode node) {
    while (node.left != null) {
        node = node.left;
    }
    return node;
}

4. Validating a Binary Search Tree

The Local Invariant Trap: It is not enough to check node.left.val < node.val && node.right.val > node.val locally! Every node in the left subtree must be less than all ancestors above it.

      5
     / \
    1   4
       / \
      3   6

In the tree above, 4.left = 3 and 4.right = 6 are locally valid around node 4, but 3 is in the right subtree of 5 (violating 3 > 5). The entire tree is invalid.

Correct Boundary Propagation Validation:

Pass valid min and max boundaries down the recursion tree:

ValidateBST.java
public boolean isValidBST(TreeNode root) {
    // Use Long.MIN_VALUE and Long.MAX_VALUE to prevent Integer overflow
    return validate(root, Long.MIN_VALUE, Long.MAX_VALUE);
}

private boolean validate(TreeNode node, long min, long max) {
    if (node == null) return true;

    if (node.val <= min || node.val >= max) {
        return false;
    }

    // Left subtree must be < node.val; Right subtree must be > node.val
    return validate(node.left, min, node.val) &&
           validate(node.right, node.val, max);
}

5. Lowest Common Ancestor (LCA) in BST

Unlike a general binary tree which requires bottom-up postorder traversal, LCA in a BST takes advantage of the sorted property in O(h) time and O(1) space:

LowestCommonAncestorBST.java
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    TreeNode curr = root;

    while (curr != null) {
        // Both nodes lie in the left subtree
        if (p.val < curr.val && q.val < curr.val) {
            curr = curr.left;
        }
        // Both nodes lie in the right subtree
        else if (p.val > curr.val && q.val > curr.val) {
            curr = curr.right;
        }
        // Split point found: curr is the LCA!
        else {
            return curr;
        }
    }

    return null;
}

Performance Summary

OperationAverage Case (Balanced BST)Worst Case (Degenerate / Skewed BST)
SearchO(log n)O(n)
InsertO(log n)O(n)
DeleteO(log n)O(n)
Inorder TraversalO(n)O(n)
Space (Recursion)O(log n)O(n)

Why Self-Balancing Is Necessary: When items are inserted in sorted order (1, 2, 3, 4, 5), an unaugmented BST degenerates into a singly-linked list with height n, reducing logarithmic operations to linear scans. To prevent this, production systems use self-balancing trees like AVL Trees and Red-Black Trees.

On this page