Hello! Welcome to your first lesson on Binary Search Trees.
In our last lesson, we explored how to serialize and deserialize a general binary tree, focusing on ways to encode its structure into a linear format. We saw that the main challenge was preserving the relationships between nodes. Today, we transition to a special, more structured type of binary tree: the Binary Search Tree (BST).
The learning outcome for this lesson is to implement search, insertion, and deletion operations in a Binary Search Tree (BST). The ordered nature of BSTs makes these operations highly efficient, and mastering their implementation is fundamental for technical interviews. We will cover the logic, implementation in Python, and complexity of each core operation.
What is a Binary Search Tree?
A Binary Search Tree imposes a crucial ordering property on top of the binary tree structure. This property is the key to its efficiency.
To formally define it, let's consult a clear definition.
The article 'Binary Search Tree(BST)' from Programiz provides a concise definition of a BST's properties. Understanding these rules is essential before we proceed.
Please read the introductory section, starting from the top down to just before 'Search Operation'. Focus on the three core properties that define a BST.
As the article states, for any given node (let's call it root) in a BST:
- All values in the
root's left subtree must be less thanroot's value. - All values in the
root's right subtree must be greater thanroot's value. - Both the left and right subtrees must themselves be binary search trees.
This recursive property ensures the entire tree is sorted, which allows us to discard half of the remaining nodes at each step of a search, much like binary search in a sorted array. This leads to an average time complexity of for its core operations.
The TreeNode Class
Before we dive into the operations, let's define the TreeNode class we'll be using in our Python examples. This is the same structure we used for general binary trees.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
1. Search Operation
Searching for a value in a BST is a direct application of its defining properties. The algorithm is straightforward:
- Start at the root.
- If the tree is empty (root is
None), the value is not found. - Compare the target value with the current node's value:
- If they are equal, you've found the node.
- If the target value is less than the current node's value, move to the left child.
- If the target value is greater than the current node's value, move to the right child.
- Repeat step 3 until the node is found or you reach a
Nonepointer.
Here are the recursive and iterative implementations in Python.
Recursive Search
def searchBST(root: TreeNode, val: int) -> TreeNode:
"""
Searches for a node with the given value in a BST recursively.
Returns the node if found, otherwise None.
"""
if not root or root.val == val:
return root
if val < root.val:
return searchBST(root.left, val)
else: # val > root.val
return searchBST(root.right, val)
Iterative Search
def searchBST_iterative(root: TreeNode, val: int) -> TreeNode:
"""
Searches for a node with the given value in a BST iteratively.
Returns the node if found, otherwise None.
"""
current = root
while current:
if val == current.val:
return current
elif val < current.val:
current = current.left
else: # val > current.val
current = current.right
return None
The iterative approach is often preferred in production code to avoid potential stack overflow on very deep (unbalanced) trees, and it has a slightly better space complexity profile, which we'll discuss later.
2. Insertion Operation
Inserting a new value into a BST follows a similar logic to searching. The goal is to find the correct null position where the new node can be placed without violating the BST properties.
Algorithm:
- Search the tree for the value you want to insert.
- The search will end at a
nullpointer (since the value isn't already in the tree). - This
nullposition is exactly where the new node should be attached.
Let's watch a video from NeetCode, which explains this process clearly and implements it in Python.
Insert into a Binary Search Tree - Leetcode 701 - Python
This video, 'Insert into a Binary Search Tree', walks through the logic for finding the insertion point and provides both recursive and iterative Python solutions.
Please watch the following segments: Finding the Insertion Point (01:27 - 02:45): Understand how traversing the tree based on value comparisons leads to the correct null spot. Recursive vs. Iterative (02:45 - 04:42): Pay attention to the discussion of how recursion handles pointer updates elegantly. Recursive Implementation (05:57 - 08:29): Follow the coding of the recursive solution. Notice how the return value from the recursive call is used to update the parent's left or right pointer.
As shown in the video, the recursive solution is particularly elegant. Here is the implementation:
def insertIntoBST(root: TreeNode, val: int) -> TreeNode:
"""
Inserts a value into the correct position in a BST.
Returns the root of the modified tree.
"""
if not root:
# Base case: found the insertion point.
return TreeNode(val)
if val < root.val:
# Recurse down the left subtree and update the left child.
root.left = insertIntoBST(root.left, val)
else: # val > root.val
# Recurse down the right subtree and update the right child.
root.right = insertIntoBST(root.right, val)
# Return the (possibly unchanged) root of the current subtree.
return root
The key insight here is the line root.left = insertIntoBST(...). When the recursion hits the base case (if not root), it returns a new TreeNode(val), which is then assigned to the parent's left or right attribute.
Test your understanding!
Imagine you start with an empty BST. What would the tree look like after you insert the following numbers in this order: 10, 5, 15, 8, 12? Draw the final tree structure.
Show answer
- Insert
10:10becomes the root. - Insert
5:5 < 10, so5becomes the left child of10. - Insert
15:15 > 10, so15becomes the right child of10. - Insert
8:8 < 10(go left),8 > 5(go right).8becomes the right child of5. - Insert
12:12 > 10(go right),12 < 15(go left).12becomes the left child of15.
The final tree would be:
10
/ \
5 15
\ /
8 12
3. Deletion Operation
Deletion is the most complex of the three operations because you must ensure the BST properties are maintained after removing a node. There are three cases to consider.
Case 1: The node to be deleted is a leaf (no children).
This is the simplest case. You can simply remove the node by setting its parent's corresponding child pointer to null.

Case 2: The node to be deleted has one child.
This is also relatively simple. You can "promote" the child to take the place of the node being deleted by updating the parent's pointer to point to the grandchild.
Case 3: The node to be deleted has two children.
This is the most complex case. You cannot simply remove the node, as it would leave two disconnected subtrees. The strategy is:
- Find a replacement node that can take the deleted node's place while preserving the BST order. The two standard choices are:
- The in-order successor: the smallest node in the right subtree.
- The in-order predecessor: the largest node in the left subtree.
- Copy the value of the replacement node into the node you intend to delete.
- Recursively call the delete function to remove the replacement node from its original position. Since the in-order successor (smallest in right subtree) has no left child, its deletion will fall into Case 1 or Case 2.

This multi-step process for Case 3 is a common focus in interviews. Let's watch a video that explains all three cases and implements a robust solution.
Delete Node in a BST - Leetcode 450 - Python
The video 'Delete Node in a BST' provides a step-by-step walkthrough of all three deletion cases, with a clear explanation of the tricky two-child scenario.
Please watch the following segments: Simpler Cases (One/Zero Children) (03:10 - 05:26): Understand how returning root.right or root.left elegantly handles these cases in a recursive function. The Two-Child Case (05:26 - 08:51): This is the most important part. Focus on the logic of finding the minimum value in the right subtree (the in-order successor) and using it as a replacement. Python Implementation (08:51 - 12:58): Watch how the logic translates into code. Pay attention to the recursive call to delete the successor node.
Here's a summary of the full recursive deletion logic in Python:
def deleteNode(root: TreeNode, key: int) -> TreeNode:
"""
Deletes a node with the given key from a BST.
Returns the root of the modified tree.
"""
if not root:
return None
# 1. Find the node to delete
if key < root.val:
root.left = deleteNode(root.left, key)
elif key > root.val:
root.right = deleteNode(root.right, key)
else: # Found the node to delete (key == root.val)
# Case 1 & 2: Node has 0 or 1 child
if not root.left:
return root.right
if not root.right:
return root.left
# Case 3: Node has two children
# Find the in-order successor (smallest value in the right subtree)
successor = root.right
while successor.left:
successor = successor.left
# Replace the node's value with the successor's value
root.val = successor.val
# Recursively delete the successor from the right subtree
root.right = deleteNode(root.right, root.val)
return root
Complexity Analysis
The efficiency of BST operations depends heavily on the tree's shape.
| Operation | Best Case (Balanced Tree) | Average Case (Random) | Worst Case (Skewed Tree) |
|---|---|---|---|
| Search | |||
| Insertion | |||
| Deletion |
- Worst Case: Occurs when the tree is "skewed" or "degenerate" (e.g., inserting sorted elements), effectively becoming a linked list. All operations degrade to .
- Space Complexity: For recursive implementations, the space complexity is due to the call stack, where is the height of the tree. In the worst case, this is . For iterative implementations, it is .
Conclusion
You have now covered the three fundamental operations that define a Binary Search Tree. Understanding not just how to implement them but why they work is crucial for solving more complex tree and data-modeling problems in interviews.
Key Takeaways:
- BST Property: The left subtree is smaller, the right subtree is larger. This is the guiding principle for all operations.
- Search: A simple traversal that discards half the tree at each step, similar to binary search.
- Insertion: Search for the correct null spot and attach the new node. The recursive implementation is notably concise.
- Deletion: The most involved operation, with three distinct cases. Deleting a node with two children requires replacing it with its in-order successor (or predecessor) and then recursively deleting that successor.
- Performance: BSTs offer average time for these operations, but this degrades to if the tree becomes unbalanced.
In our next lesson, we will tackle a classic interview question that builds directly on what you've learned today: validating if a given binary tree is a valid BST. This will test your understanding of the BST properties in a subtle and interesting way.