Binary Search Tree
A binary search tree (BST) is a hierarchical data structure where each node has at most two children, and for every node the left subtree contains only values less than the node, while the right subtree contains only values greater. This ordering property enables efficient search, insertion, and deletion operations that run in O(log n) time on average.
How BST Works
A Binary Search Tree is a rooted binary tree that satisfies the BST property: for every node N, all values in N's left subtree are strictly less than N's value, and all values in N's right subtree are strictly greater. This invariant is maintained after every insert and delete operation.
The key operations on a BST are:
- Search: Start at the root. If the target equals the current node, you are done. If it is smaller, recurse left; if larger, recurse right. Each comparison eliminates roughly half the remaining nodes, yielding O(log n) average time.
- Insert: Follow the same path as search until you reach a null child pointer. Place the new node there. The tree grows by one leaf each time.
- Delete: There are three cases to handle:
- Leaf node: Simply remove it.
- One child: Replace the node with its single child.
- Two children: Find the inorder successor (smallest node in the right subtree), copy its value into the node being deleted, then delete the successor from the right subtree.
- Inorder traversal: Visit left subtree, then the node, then right subtree. This produces values in sorted (ascending) order.
- Preorder traversal: Visit the node first, then left subtree, then right subtree. Useful for creating a copy of the tree.
Important caveat: If elements are inserted in sorted order, the BST degenerates into a linked list, and all operations become O(n). Self-balancing trees such as AVL trees and Red-Black trees address this problem by automatically rebalancing after mutations.
Interactive Visualization
Explore BST operations below. Red nodes are currently being visited or compared, and green nodes have been found or just processed. Enter a value and choose an operation to watch the BST algorithm step by step.
Code Implementation
Below are BST insert and search implementations in three languages. The recursive approach mirrors the tree's own structure and is the most natural way to express BST operations.
class BSTNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def insert(root, val):
"""Insert a value into the BST and return the root."""
if root is None:
return BSTNode(val)
if val < root.val:
root.left = insert(root.left, val)
elif val > root.val:
root.right = insert(root.right, val)
return root
def search(root, val):
"""Return the node containing val, or None if not found."""
if root is None or root.val == val:
return root
if val < root.val:
return search(root.left, val)
return search(root.right, val)
def inorder(root):
"""Return a list of values in sorted (inorder) order."""
if root is None:
return []
return inorder(root.left) + [root.val] + inorder(root.right)
# Example usage
root = None
for v in [50, 30, 70, 20, 40, 60, 80]:
root = insert(root, v)
print(inorder(root)) # [20, 30, 40, 50, 60, 70, 80]
print(search(root, 40)) # <BSTNode object>
print(search(root, 99)) # None
class BSTNode {
int val;
BSTNode left, right;
BSTNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
public class BST {
private BSTNode root;
/** Insert a value into the BST. */
public void insert(int val) {
root = insertRec(root, val);
}
private BSTNode insertRec(BSTNode node, int val) {
if (node == null) return new BSTNode(val);
if (val < node.val)
node.left = insertRec(node.left, val);
else if (val > node.val)
node.right = insertRec(node.right, val);
return node;
}
/** Search for a value. Returns true if found. */
public boolean search(int val) {
return searchRec(root, val);
}
private boolean searchRec(BSTNode node, int val) {
if (node == null) return false;
if (val == node.val) return true;
if (val < node.val) return searchRec(node.left, val);
return searchRec(node.right, val);
}
public static void main(String[] args) {
BST tree = new BST();
int[] values = {50, 30, 70, 20, 40, 60, 80};
for (int v : values) tree.insert(v);
System.out.println(tree.search(40)); // true
System.out.println(tree.search(99)); // false
}
}
#include <iostream>
using namespace std;
struct BSTNode {
int val;
BSTNode* left;
BSTNode* right;
BSTNode(int v) : val(v), left(nullptr), right(nullptr) {}
};
/** Insert a value into the BST and return the root. */
BSTNode* insert(BSTNode* root, int val) {
if (!root) return new BSTNode(val);
if (val < root->val)
root->left = insert(root->left, val);
else if (val > root->val)
root->right = insert(root->right, val);
return root;
}
/** Search for a value. Returns the node or nullptr. */
BSTNode* search(BSTNode* root, int val) {
if (!root || root->val == val) return root;
if (val < root->val) return search(root->left, val);
return search(root->right, val);
}
/** Print inorder traversal. */
void inorder(BSTNode* root) {
if (!root) return;
inorder(root->left);
cout << root->val << " ";
inorder(root->right);
}
int main() {
BSTNode* root = nullptr;
int values[] = {50, 30, 70, 20, 40, 60, 80};
for (int v : values) root = insert(root, v);
inorder(root); // 20 30 40 50 60 70 80
cout << endl;
cout << (search(root, 40) ? "Found" : "Not found") << endl; // Found
cout << (search(root, 99) ? "Found" : "Not found") << endl; // Not found
return 0;
}
Complexity Analysis
BST operation performance depends on the height of the tree. For a balanced tree with n nodes, the height is O(log n), giving logarithmic performance. In the worst case (a degenerate/skewed tree), the height becomes O(n), and every operation degrades to linear time.
| Operation | Average Case | Worst Case | Description |
|---|---|---|---|
| Search | O(log n) | O(n) | Balanced tree halves search space each step; skewed tree traverses all nodes |
| Insert | O(log n) | O(n) | Follows search path to find insertion point at a leaf position |
| Delete | O(log n) | O(n) | Search for node plus possible successor lookup, both bounded by tree height |
| Space | O(n) | Each of the n values is stored in exactly one node | |
Self-balancing variants like AVL trees and Red-Black trees guarantee O(log n) height through rotations after insertions and deletions, ensuring worst-case O(log n) for all operations. In practice, randomly ordered insertions produce a tree with expected height of about 1.39 log2(n), which is close to optimal.
Quiz
Test your understanding of binary search trees with these questions.
Q1. What is the BST property?
Q2. What traversal of a BST produces values in sorted ascending order?
Q3. When deleting a node with two children from a BST, what is the standard approach?
Q4. If you insert the values [1, 2, 3, 4, 5] into an initially empty BST in that order, what is the height of the resulting tree?
Q5. What is the worst-case time complexity of searching in a BST with n nodes?