Introduction
Welcome to the final lesson in our module on Trees and Binary Search Trees! In our last session, we analyzed how the structure of a BST—specifically whether it's balanced or unbalanced—dramatically affects the performance of its operations. We concluded that searching, inserting, and deleting all have a time complexity of , which can be as efficient as or as slow as .
Today, we'll apply this knowledge to achieve our final learning outcome for this module: to solve a practice problem involving a BST, such as finding the minimum value. This is a classic interview question that neatly ties together the core properties of a BST. By the end of this lesson, you'll be able to implement two different solutions for this problem and analyze their trade-offs.
Based on what we've learned, where in a BST do you think the smallest element must reside?
The Core Logic: The Leftmost Node
The defining property of a Binary Search Tree is its inherent order:
- For any given node, all values in its left subtree are smaller.
- For any given node, all values in its right subtree are larger.
This rule provides a straightforward algorithm for finding the minimum value. To find the smallest possible value, we should always choose the smaller path. Therefore, starting from the root, we just need to follow the left child pointers until we can't go any further. The node we end up at—the one with no left child—must contain the minimum value in the entire tree.
This simple but powerful logic is the key.

This task is a common exercise when learning about BSTs. The following video sets it up as a challenge.
Binary Tree Part 1 | BST | Binary Search Tree - Data Structures & Algorithms Tutorials In Python #10
The codebasics channel presents finding the minimum and maximum elements as a natural next step after learning to build a BST. This short clip states the problem and gives a direct hint about the solution.
Watch from 23:40 to 24:15. The presenter confirms that the minimum element is the leftmost leaf (or node) in the tree.
Now, let's explore how to turn this logic into code. We'll look at two common implementations: iterative and recursive.
Approach 1: The Iterative Solution
The most direct way to implement "keep going left" is with a simple loop. This is an iterative approach.
The algorithm is as follows:
- Check if the tree is empty (i.e., the root is
None). If so, there is no minimum value. - Start with a
currentpointer at the root node. - Use a
whileloop that continues as long ascurrent.leftis notNone. - Inside the loop, move to the left:
current = current.left. - When the loop finishes,
currentis pointing to the leftmost node. Returncurrent.data.
This approach is efficient and easy to understand. Let's look at a clear Python implementation.
Writing a Binary Search Tree in Python With Examples
The Boot.dev article 'Writing a Binary Search Tree in Python' provides a complete BST implementation, including a concise get_min method that uses this iterative logic.
Read the section 'Step 3 - Get Min and Get Max'. Focus on the get_min function. Notice how it uses a while loop to traverse down the left side of the tree.
Complexity Analysis
- Time Complexity: The loop runs as many times as there are levels to traverse to reach the leftmost node. In the worst case, this is the height of the tree, . So, the time complexity is . As we learned, this is for a balanced tree and for a skewed tree.
- Space Complexity: This approach is very memory-efficient. We only use one extra variable (
current) to keep track of the node. The space used does not grow with the size of the tree. Therefore, the space complexity is , or constant space.
Approach 2: The Recursive Solution
Since a tree is a recursive data structure, we can also solve this problem using recursion. This often leads to elegant, though not always the most space-efficient, code.
The recursive logic is:
- Base Case: If the current node has no left child, then it is the minimum value in the subtree we're looking at. We've found our answer, so we return its value.
- Recursive Step: If the current node does have a left child, then we know the minimum value must be in that left subtree. So, we call the function again on the left child (
find_min(node.left)).
The following video explains this recursive formulation clearly.
Find min and max element in a binary search tree
The mycodeschool video 'Find min and max element in a binary search tree' walks through both the iterative and recursive solutions. We'll focus on the recursive part.
Watch from 04:13 to 05:41. The video explains the recursive logic step-by-step. Although the code is in C++, the logic of the base case and recursive call is universal and clearly illustrated.
Here is what the same logic looks like in Python, as shown in the GeeksforGeeks article, which provides multiple approaches.
Minimum in a Binary Search Tree - GeeksforGeeks
The article 'Minimum in a Binary Search Tree' on GeeksforGeeks presents several ways to solve this. The recursive method is described under 'Better Approach'.
Read the section titled '[Better Approach] Using Recursion'. Compare the Python code provided there with the logic from the video you just watched.
Complexity Analysis
- Time Complexity: Just like the iterative version, the number of recursive calls is equal to the height of the path to the leftmost node. The time complexity is .
- Space Complexity: Each recursive call adds a new frame to the call stack. In the worst case, the number of active calls at one time is equal to the height of the tree, . Therefore, the space complexity is . This is less space-efficient than the iterative solution's space.
Practice Exercise
Now it's your turn to write the code. Below is the BinarySearchTreeNode class we have been working with. Your task is to add two methods to it: find_min() and find_max().
- Copy the code into a Python file (e.g.,
bst_min_max.py). - Implement the
find_min()method using the iterative approach we discussed. - Implement the
find_max()method. The logic is symmetrical to finding the minimum—you just need to go right instead of left. - Run the file to test your implementation. It should print
Minimum value: 3andMaximum value: 24.
class BinarySearchTreeNode:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def add_child(self, data):
if data == self.data:
return # Node already exists
if data < self.data:
# Add to left subtree
if self.left:
self.left.add_child(data)
else:
self.left = BinarySearchTreeNode(data)
else:
# Add to right subtree
if self.right:
self.right.add_child(data)
else:
self.right = BinarySearchTreeNode(data)
# --- YOUR CODE HERE ---
def find_min(self):
# Implement the iterative solution to find the minimum value.
# Start at the current node ('self') and loop down the left side.
# Handle the case where the tree might be empty (though our test case isn't).
pass # Replace this with your implementation
def find_max(self):
# Implement the iterative solution to find the maximum value.
# The logic is the mirror image of find_min().
pass # Replace this with your implementation
# --- END OF YOUR CODE ---
def build_tree(elements):
"""Helper function to build a tree from a list of elements."""
if not elements:
return None
root = BinarySearchTreeNode(elements[0])
for i in range(1, len(elements)):
root.add_child(elements[i])
return root
# --- Main part of the script ---
if __name__ == '__main__':
numbers = [12, 6, 18, 3, 11, 19, 5, 24]
bst_root = build_tree(numbers)
# Test your methods
min_val = bst_root.find_min()
print(f"Minimum value: {min_val}")
max_val = bst_root.find_max()
print(f"Maximum value: {max_val}")
Click here to see the solution
def find_min(self):
"""Finds the smallest value in the tree."""
current = self
# Loop down to find the leftmost leaf
while current.left is not None:
current = current.left
return current.data
def find_max(self):
"""Finds the largest value in the tree."""
current = self
# Loop down to find the rightmost leaf
while current.right is not None:
current = current.right
return current.data
Conclusion
Congratulations on completing the module on Trees and Binary Search Trees! Today, you successfully implemented a solution to a classic algorithm problem, which is a great skill for technical interviews.
Key Takeaways:
- The minimum value in a BST is always the leftmost node, and the maximum value is the rightmost node.
- This can be found using an iterative approach with a
whileloop, which has an efficient space complexity of . - It can also be solved recursively, which can lead to more concise code but uses space for the call stack.
- Both methods have a time complexity of , directly linking back to our previous lesson on the importance of tree balance for performance.
Preview of the Next Lesson:
We have now covered several fundamental data structures: arrays/lists, linked lists, stacks, queues, hash tables, and trees. You're building a strong foundation. In the next module, we will begin our exploration of graphs. We'll start by defining what graphs are, the different types that exist, and how we can represent these complex networks in Python code.