A Binary Search Tree is a fundamental node-based data structure used for fast data storage, retrieval, and sorting. It consists of a root node, where each node can have at most two children (left and right).
Because of the Golden Rule, searching and inserting in a BST is highly efficient. At each step, you compare the target value with the current node:
Removing a node from a BST is more complex than inserting because you must preserve the Golden Rule. There are three distinct cases when deleting a target node:
Unlike arrays, there are multiple ways to iterate through a tree depending on the order in which you visit the Root, Left, and Right nodes.
Before exploring the traversal algorithms, it is important to understand how a single node is represented in code. In Python, each node is typically an object containing its value and pointers to its left and right children:
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
This method always visits nodes in ascending, sorted order. It is the most commonly used traversal for BSTs.
Why it works: Because a BST stores smaller values on the left and larger on the right, forcing the algorithm to explore Left ➔ Root ➔ Right guarantees we read the data in perfectly sorted order.
def in_order(node):
if node is not None:
in_order(node.left)
print(node.value, end=" ")
in_order(node.right)
This method captures the top-down structure of the tree. It is mainly used for creating a copy of the tree or serializing it to save in a file.
Why it works: By recording the parent before its children, we capture the exact hierarchy. If you insert values into a new, empty tree following a pre-order sequence, you will perfectly recreate the original tree's shape.
def pre_order(node):
if node is not None:
print(node.value, end=" ")
pre_order(node.left)
pre_order(node.right)
This method dives all the way down to the leaves and processes nodes from the bottom up. It is strictly used for safely deleting a tree.
Why it works: A node is processed only after all of its descendants have been processed. If you are freeing memory (deleting nodes), this ensures you never delete a parent node while it still holds active pointers to its children.
def post_order(node):
if node is not None:
post_order(node.left)
post_order(node.right)
print(node.value, end=" ")
4
/ \
2 6
/ \ / \
1 3 5 7
Depending on the algorithm you choose, the output sequence will be completely different:
1, 2, 3, 4, 5, 6, 7 (Perfectly sorted)
4, 2, 1, 3, 6, 5, 7
(Explores roots before leaves)
1, 3, 2, 5, 7, 6, 4
(Explores leaves before roots)
The efficiency of a BST depends entirely on its height. If you insert sorted data (e.g., 1, 2, 3, 4, 5) into a standard BST, every new node goes to the right. The tree becomes highly lopsided, effectively degrading into a standard Linked List.
To prevent this, computer scientists use Self-Balancing Binary Search Trees (like AVL Trees or Red-Black Trees), which automatically rotate their nodes during insertion to keep the tree's height as short as possible.
| Operation | Average Case | Worst Case (Unbalanced) |
|---|---|---|
| Access / Search | $O(\log n)$ | $O(n)$ |
| Insertion | $O(\log n)$ | $O(n)$ |
| Deletion | $O(\log n)$ | $O(n)$ |
| Space Complexity | $O(n)$ | $O(n)$ |