Back to Data Structures & Algorithms
2026-04-026 min read

Search Trees (Data Structures & Algorithms)

Learn Search Trees (Data Structures & Algorithms) step by step with clear examples and exercises.

Title: Search Trees (Data Structures & Algorithms)

Why This Matters

Search trees are essential data structures used in computer science to efficiently store, retrieve, and manipulate data. They are fundamental in various applications such as databases, operating systems, and artificial intelligence. Understanding search trees can help you solve complex problems quickly and optimally, making them crucial for interviews, exams, and real-world programming tasks.

Prerequisites

To follow this lesson, you should have a basic understanding of the following concepts:

  1. Basic Python syntax (variables, data types, loops, functions)
  2. Recursion
  3. Binary trees
  4. Time complexity analysis

Core Concept

A search tree is a binary tree with the additional property that each node has a unique key, and all keys in the left subtree are less than the key at the root, while all keys in the right subtree are greater than or equal to it. This property allows for efficient searching, insertion, and deletion of data.

Binary Search Tree (BST)

A binary search tree is a common type of search tree. It implements the binary search algorithm, which divides the search interval in half at each step, making it an efficient method for finding specific values.

Insertion

To insert a new node into a BST, follow these steps:

  1. Start at the root of the tree.
  2. If the tree is empty, create a new node and set it as the root.
  3. Otherwise, compare the key of the new node with the key at the current node.
  • If the new key is less than the current key, move to the left child. Repeat this process until you find an empty spot or reach a leaf node. Insert the new node there.
  • If the new key is greater than or equal to the current key, move to the right child. Repeat this process until you find an empty spot or reach a leaf node. Insert the new node there.
  1. Balance the tree if necessary (e.g., by rotating nodes) to maintain its properties.

Searching

To search for a specific value in a BST, follow these steps:

  1. Start at the root of the tree.
  2. Compare the key you're searching for with the current node's key.
  • If the key is less than the current key, move to the left child and repeat this process until you find the value or reach a leaf node with no match.
  • If the key is greater than or equal to the current key, move to the right child and repeat this process until you find the value or reach a leaf node with no match.
  1. Return the found node if it exists; otherwise, return None.

Deletion

To delete a node from a BST, follow these steps:

  1. Find the node to be deleted.
  2. If the node has no children, remove it and continue with its parent's subtree.
  3. If the node has one child, replace it with its child.
  4. If the node has two children, find its inorder successor (the smallest key in the right subtree) and replace the deleted node's key with the inorder successor's key. Repeat this process recursively to delete the inorder successor from the tree.
  5. Balance the tree if necessary after deletion to maintain its properties.

Worked Example

Let's create a binary search tree and perform some operations on it:

class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None

def insert(root, key):
if root is None:
return Node(key)
else:
if root.key < key:
root.right = insert(root.right, key)
else:
root.left = insert(root.left, key)
return root

def search(root, key):
if root is None or root.key == key:
return root
elif root.key < key:
return search(root.right, key)
else:
return search(root.left, key)

def delete(root, key):
if root is None:
return root
else:
if root.key < key:
root.right = delete(root.right, key)
elif root.key > key:
root.left = delete(root.left, key)
else:
if root.left is None:
return root.right
elif root.right is None:
return root.left
min_value = find_min_value(root.right)
root.key = min_value
root.right = delete(root.right, min_value)
return root

def find_min_value(node):
current = node
while current.left is not None:
current = current.left
return current.key

Now let's create a binary search tree and insert some values:

root = None
values = [50, 30, 70, 20, 40, 60, 80]
for value in values:
root = insert(root, value)

Now the tree looks like this:

50
/ \
30 70
/ \ \
20 40 80

We can search for values and delete nodes as needed:

print(search(root, 50)) # Output: Node(50)
print(search(root, 90)) # Output: None

root = delete(root, 20)
root = delete(root, 30)
root = delete(root, 40)

Now the tree looks like this:

50
/ \
70 80

Common Mistakes

  1. Inserting a duplicate key will create multiple nodes with the same key, which violates the unique key property of a search tree. Make sure to check for duplicates before insertion and update existing nodes instead.
  2. Forgetting to balance the tree after deletion can cause it to become unbalanced, leading to inefficient operations. Use techniques like AVL trees or red-black trees to maintain balance.
  3. Not properly handling cases where a node has only one child during deletion can lead to incorrect results or memory leaks. Make sure to handle these cases correctly.
  4. Using an unsorted linked list instead of a search tree for efficient searching, inserting, and deleting operations will result in slower performance.
  5. Failing to define the __init__ method for the Node class can cause issues with object creation and property assignment.

Practice Questions

  1. Implement the inorder_traversal function that prints the keys of a binary search tree in sorted order.
  2. Implement the height function that calculates the height of a binary search tree (the number of levels from the root to the deepest leaf).
  3. Implement the minimum function that finds the smallest key in a binary search tree.
  4. Implement the maximum function that finds the largest key in a binary search tree.
  5. Implement the preorder_traversal function that prints the keys of a binary search tree in the order: root, left subtree, right subtree.

FAQ

Q: What is the time complexity for searching, inserting, and deleting in a binary search tree?

A: Searching has an average time complexity of O(log n), while insertion and deletion have an average time complexity of O(log n) as well, but they can be O(n) in the worst case.

Q: Why are search trees more efficient than linear data structures for searching, inserting, and deleting operations?

A: Search trees maintain a hierarchical structure that allows for quicker searches by reducing the number of comparisons needed to find a specific value.

Q: What is the difference between a binary search tree and a balanced binary search tree?

A: A binary search tree has no guarantees about its height, while a balanced binary search tree (such as AVL or red-black trees) ensures that the height of the tree is approximately logarithmic in the number of nodes. This makes balanced binary search trees more efficient for searching, inserting, and deleting operations.

Q: How do I implement a self-balancing binary search tree in Python?

A: Implementing a self-balancing binary search tree like AVL or red-black trees can be complex. You may want to use an existing library that provides these data structures, such as the avl module from the python-data-structures package.

Q: What are some other types of search trees besides binary search trees?

A: Other types of search trees include multiway search trees (such as B-trees and B+ trees), trie, and hash trees. Each type has its own advantages and is suitable for specific use cases.

Search Trees (Data Structures & Algorithms) | Data Structures & Algorithms | XQA Learn