Binary Search Trees
A Binary Search Tree (BST) is a binary tree with the ordering property: for every node, all values in the left subtree are smaller and all values in the right subtree are larger. This enables O(log n) search, insert, and delete on balanced trees.
The BST Property
A Binary Search Tree maintains the following invariant for every node N:
- All values in N's left subtree are less than N's value - All values in N's right subtree are greater than N's value
This property holds recursively for every node in the tree. It enables efficient search: at each node, you can eliminate half the remaining tree by comparing the target with the current node's value.
Average case time complexity (balanced tree): - Search: O(log n) - Insert: O(log n) - Delete: O(log n)
Worst case (degenerate/unbalanced tree, e.g., inserting sorted data): O(n)
BST Operations: Search, Insert, Delete
The three fundamental BST operations:
class BST:
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def __init__(self):
self.root = None
# ---- Search - O(log n) average ----
def search(self, val):
return self._search(self.root, val)
def _search(self, node, val):
if node is None or node.val == val:
return node
if val < node.val:
return self._search(node.left, val)
return self._search(node.right, val)
# ---- Insert - O(log n) average ----
def insert(self, val):
self.root = self._insert(self.root, val)
def _insert(self, node, val):
if node is None:
return self.Node(val)
if val < node.val:
node.left = self._insert(node.left, val)
elif val > node.val:
node.right = self._insert(node.right, val)
# Duplicate: ignore (or handle as needed)
return node
# ---- Delete - O(log n) average ----
def delete(self, val):
self.root = self._delete(self.root, val)
def _delete(self, node, val):
if node is None:
return None
if val < node.val:
node.left = self._delete(node.left, val)
elif val > node.val:
node.right = self._delete(node.right, val)
else:
# Case 1: Leaf node
if not node.left and not node.right:
return None
# Case 2: One child
if not node.left:
return node.right
if not node.right:
return node.left
# Case 3: Two children - replace with inorder successor
successor = self._min_node(node.right)
node.val = successor.val
node.right = self._delete(node.right, successor.val)
return node
def _min_node(self, node):
while node.left:
node = node.left
return node
def inorder(self):
result = []
def _inorder(node):
if node:
_inorder(node.left)
result.append(node.val)
_inorder(node.right)
_inorder(self.root)
return result
# Test
bst = BST()
for val in [5, 3, 7, 1, 4, 6, 8]:
bst.insert(val)
print("Inorder:", bst.inorder()) # [1, 3, 4, 5, 6, 7, 8] (sorted!)
bst.delete(3)
print("After deleting 3:", bst.inorder()) # [1, 4, 5, 6, 7, 8]
print("Search 6:", bst.search(6) is not None) # TrueBST Validation and Floor/Ceiling
Important BST algorithms:
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
# ---- Validate BST ----
def is_valid_bst(root: Optional[TreeNode]) -> bool:
def validate(node, min_val, max_val):
if not node:
return True
if not (min_val < node.val < max_val):
return False
return (validate(node.left, min_val, node.val) and
validate(node.right, node.val, max_val))
return validate(root, float('-inf'), float('inf'))
# ---- Floor (largest value <= target) ----
def floor_bst(root, target):
floor = None
while root:
if root.val == target:
return root.val
elif root.val < target:
floor = root.val # Candidate
root = root.right
else:
root = root.left
return floor
# ---- Kth Smallest Element ----
def kth_smallest(root, k):
stack = []
current = root
count = 0
while current or stack:
while current:
stack.append(current)
current = current.left
current = stack.pop()
count += 1
if count == k:
return current.val
current = current.rightKey Takeaways
- BST property: left subtree values < node < right subtree values, enabling O(log n) operations.
- Deletion of a node with two children replaces it with its inorder successor (smallest in right subtree).
- Inorder traversal of a BST always produces elements in sorted order.
- Worst-case BST degrades to O(n) when data is inserted in sorted order — use AVL or Red-Black trees.
- Validate a BST by passing min/max bounds down the recursion, not just comparing with immediate children.
Contact Us
Have a question or feedback? Fill out the form below or reach us directly at support@nvaitraining.com