Key Takeaways: A Binary Search Tree (BST) is a special type of Binary Tree. It automatically keeps data sorted by ensuring that all values smaller than a node go to its Left, and all values larger go to its Right.
In previous tutorials, we learned about basic Binary Trees. A regular Binary Tree can hold data in any random order.
However, if we want to find a specific item in a random tree, we have to search every single node. This is slow!
To fix this, Computer Scientists created the Binary Search Tree.
A Binary Search Tree is just a normal Binary Tree that strictly follows one golden rule.
For every single node in the tree:
Binary Search Tree: Left is always smaller, Right is always larger.
A BST works exactly like the Binary Search algorithm we learned for Arrays.
Imagine you are looking for the number 60 in the tree above.
50. 60 greater than or less than 50? It is greater!70.60 greater than or less than 70? It is less!60!With just two comparisons, we eliminated over half of the tree. This gives searching a blistering O(log n) time complexity!
How do we build a Binary Search Tree? We insert nodes one by one, letting them "fall" into their correct places.
Let's insert 45 into the tree above:
50. 45 is less than 50, so go Left.30. 45 is greater than 30, so go Right.40. 45 is greater than 40, so go Right.40! We create a new node for 45 here.Deleting a node is the hardest operation in a BST because we have to keep the tree connected and sorted.
There are 3 specific cases for deletion:
This is easy! If you want to delete 20, you just remove it. The tree remains perfectly valid.
If you want to delete 70 (assuming it only had 60 as a child), you just delete 70 and connect its parent (50) directly to its child (60).
If you want to delete the Root (50), you can't just delete it, or the whole tree splits in half!
Instead, you find its In-Order Successor.
Let's look at how to implement the Node structure, Insert method, and Search method in Python.
# 1. Create the TreeNode Class class TreeNode: def __init__(self, key): self.left = None self.right = None self.val = key# 2. Insert Function def insert(root, key): # If tree is empty, return a new node if root is None: return TreeNode(key) # Otherwise, recurse down the tree if key < root.val: root.left = insert(root.left, key) elif key > root.val: root.right = insert(root.right, key) # Return the (unchanged) node pointer return root
# 3. Search Function def search(root, key): # Base Cases: root is null or key is present at root if root is None or root.val == key: return root # Key is greater than root's key, search Right if root.val < key: return search(root.right, key) # Key is smaller than root's key, search Left return search(root.left, key)
# Let's test it out! root = None root = insert(root, 50) insert(root, 30) insert(root, 20) insert(root, 40) insert(root, 70) insert(root, 60) insert(root, 80)
# Search for a value result = search(root, 60) if result: print("Found:", result.val) else: print("Value not found in BST.")
O(log n). Because we eliminate half of the tree with every step, searching, inserting, and deleting are exceptionally fast.O(n). If you insert data that is already sorted (e.g., 10, 20, 30, 40), the tree just forms a straight line! It basically degrades into a slow Linked List. O(n) to hold the nodes, plus O(h) for the recursive call stack (where h is the tree's height).What is the primary rule that makes a tree a Binary Search Tree (BST)?
What is the worst-case Time Complexity for searching in a completely unbalanced BST (a straight line)?