Report Content
×Binary Search Trees: Operations, Balancing & Applications
Master BST operations, balancing techniques, and real-world applications. This tutorial covers insertion, deletion, traversal, and self-balancing trees with implementation examples.
BST Usage in Systems (2023)
1. BST Fundamentals
Definition
A Binary Search Tree (BST) is a binary tree where:
- All values in the left subtree are smaller than the node.
- All values in the right subtree are greater than the node.
Core Properties:
- Ordering: Left subtree ≤ Node ≤ Right subtree
- No Duplicates: Typically stores unique values
- Efficiency: O(h) operations, where h = tree height
How It Works
1
Search
- Start at the root node.
- If target == node → return found.
- If target < node → go left child.
- If target > node → go right child.
- Repeat until found or null.
2
Insert
- Start at the root node.
- If value < node → move left.
- If value > node → move right.
- Insert into empty position.
3
Delete
- Find the node to remove.
- Leaf node → remove directly.
- One child → replace with child.
- Two children → replace with inorder successor.
Time Complexities:
| Operation | Average Case | Worst Case (Unbalanced) | Balanced Tree |
|---|---|---|---|
| Search | O(log n) | O(n) | O(log n) |
| Insertion | O(log n) | O(n) | O(log n) |
| Deletion | O(log n) | O(n) | O(log n) |
| Traversal | O(n) | O(n) | O(n) |
Node Implementation:
class BSTNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
class BinarySearchTree:
def __init__(self):
self.root = None
2. Core BST Operations
Essential Methods:
Insertion
def insert(self, value):
new_node = BSTNode(value)
if self.root is None:
self.root = new_node
return
current = self.root
while True:
if value < current.value:
if current.left is None:
current.left = new_node
break
current = current.left
elif value > current.value:
if current.right is None:
current.right = new_node
break
current = current.right
else:
break
Deletion (3 Cases)
def delete(self, value):
node, parent = self._find_with_parent(value)
if not node:
return False
# Case 1: No children
if not node.left and not node.right:
self._replace_child(parent, node, None)
# Case 2: One child
elif not node.left or not node.right:
child = node.left or node.right
self._replace_child(parent, node, child)
# Case 3: Two children
else:
successor = self._find_min(node.right)
node.value = successor.value
self.delete(successor.value)
return True
def _find_min(self, node):
while node.left:
node = node.left
return node
def _replace_child(self, parent, old_child, new_child):
if not parent:
self.root = new_child
elif parent.left == old_child:
parent.left = new_child
else:
parent.right = new_child
Search Techniques:
- Iterative Search: Efficient O(h) implementation
- Recursive Search: Cleaner code but uses call stack
- Range Queries: Find all values between [x, y]
- Successor/Predecessor: In-order traversal helpers
3. Balancing BSTs
Self-Balancing Variants:
AVL Trees
Height-balanced with rotations
class AVLTree(BinarySearchTree):
def insert(self, value):
super().insert(value)
self._balance(self.root)
def _balance(self, node):
if not node:
return
balance_factor = self._get_balance_factor(node)
# Left heavy
if balance_factor > 1:
if self._get_balance_factor(node.left) >= 0:
self._rotate_right(node)
else:
self._rotate_left(node.left)
self._rotate_right(node)
# Right heavy
elif balance_factor < -1:
if self._get_balance_factor(node.right) <= 0:
self._rotate_left(node)
else:
self._rotate_right(node.right)
self._rotate_left(node)
self._balance(node.left)
self._balance(node.right)
def _get_balance_factor(self, node):
if not node:
return 0
return self._get_height(node.left) - self._get_height(node.right)
def _get_height(self, node):
if not node:
return 0
return 1 + max(self._get_height(node.left), self._get_height(node.right))
def _rotate_right(self, node):
left_child = node.left
node.left = left_child.right
left_child.right = node
return left_child
def _rotate_left(self, node):
right_child = node.right
node.right = right_child.left
right_child.left = node
return right_child
Red-Black Trees
Color-coded nodes with rules
class RedBlackTree(BinarySearchTree):
def insert(self, value):
new_node = super().insert(value)
new_node.color = "RED"
self._fix_insert_violation(new_node)
def _fix_insert_violation(self, node):
while node != self.root and node.parent.color == "RED":
# Complex recoloring and rotation logic
pass
self.root.color = "BLACK"
Balancing Operations:
- Left Rotation: Pivot around right child
- Right Rotation: Pivot around left child
- Left-Right Rotation: Double rotation
- Right-Left Rotation: Mirror of above
BST Problem-Solving Cheat Sheet
| Problem Type | Approach | Time Complexity |
|---|---|---|
| Validate BST | In-order traversal check | O(n) |
| Kth Smallest | In-order traversal with count | O(k) |
| Lowest Common Ancestor | Utilize BST properties | O(h) |
| Range Sum | Modified traversal | O(k) |
4. Advanced BST Concepts
Threaded BST
Optimized for in-order traversal
class ThreadedBSTNode(BSTNode):
def __init__(self, value):
super().__init__(value)
self.left_thread = False
self.right_thread = False
B-Tree Variants
Optimized for disk storage
class BTreeNode:
def __init__(self, order):
self.keys = []
self.children = []
self.order = order
self.is_leaf = True
Augmented BST
Additional node metadata
class AugmentedBSTNode(BSTNode):
def __init__(self, value):
super().__init__(value)
self.size = 1 # Subtree node count
self.sum = value # Subtree sum
BST Problem-Solving Checklist
Verify BST property maintenance
Consider balance factor for performance
Handle all deletion cases (0, 1, 2 children)
Utilize in-order traversal for sorted output
DSA Expert Insight: BST problems appear in 35% of technical interviews. Mastering both basic operations and balancing techniques is crucial for efficient search and retrieval problems.
You need to be logged in to participate in this discussion.
×