Implementing a Binary Search Tree in Python

Introduction

A Binary Search Tree (BST) is a tree-based data structure that has the following properties:

  • Each node has a unique key.
  • The left subtree of a node contains only nodes with keys less than the node’s key.
  • The right subtree of a node contains only nodes with keys greater than the node’s key.

In this blog post, we’ll dive deep into implementing a Binary Search Tree in Python.

Structure of a Binary Search Tree

A simple Binary Search Tree can be visually depicted like this:

        15
       /  \
      10   20
     / \   / \
    5  12 18  25

Here, 15 is the root node, and every node in its left subtree, like 10, 5, and 12, is smaller than 15. Every node in its right subtree, such as 20, 18, and 25, is greater than 15.

Python Implementation

Let’s implement a basic Binary Search Tree in Python.

The Node Class

Firstly, we’ll start by creating a Node class to represent each node in the BST.

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

The BinarySearchTree Class

Now we will implement the BinarySearchTree class, which will encapsulate the behaviors of our tree.

class BinarySearchTree:
    def __init__(self):
        self.root = None

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

    def delete(self, root, key):
        # Base case: the key is not found in the tree
        if root is None:
            return root

        # Recursive case: traverse the tree to find the node to be deleted
        if key < root.val:
            root.left = self.delete(root.left, key)
        elif key > root.val:
            root.right = self.delete(root.right, key)
        else:  # root.val == key, this is the node to be deleted
            # Node with only one child or no child
            if root.left is None:
                temp = root.right
                root = None
                return temp
            elif root.right is None:
                temp = root.left
                root = None
                return temp

            # Node with two children, find the inorder successor
            root.val = self.minValueNode(root.right).val

            # Delete the inorder successor
            root.right = self.delete(root.right, root.val)

        return root

    def minValueNode(self, root):
        current = root
        while current.left is not None:
            current = current.left
        return current

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

    def inorder_traversal(self, root):
        if root:
            self.inorder_traversal(root.left)
            print(root.val, end=" ")
            self.inorder_traversal(root.right)

Method Details:

  • insert: Adds a new node with a given key to the tree.
  • delete: Removes a node with a given key from the tree and returns the new root.
  • minValueNode: Helper function to find the node with the smallest value greater than the given node.
  • search: Searches for a node with a given key in the tree.
  • inorder_traversal: In-order traversal of the tree, which prints the tree nodes in sorted order.

Deleting a node in a Binary Search Tree (BST) is a bit more complex than inserting or searching for a node. There are three main scenarios to consider when deleting a node:

  1. Leaf Node: The node to be deleted is a leaf node. Simply remove it from the tree.
  2. One Child: The node to be deleted has one child. Remove the node and link its parent to its child.
  3. Two Children: The node to be deleted has two children. Find the inorder successor (the smallest node in its right subtree) and replace the node with it. Then, delete the inorder successor.

Putting It All Together

Let’s use the above code to create a Binary Search Tree.

# Create a new Binary Search Tree
bst = BinarySearchTree()
root = None

# Insert elements
root = bst.insert(root, 15)
bst.insert(root, 10)
bst.insert(root, 20)
bst.insert(root, 5)
bst.insert(root, 12)
bst.insert(root, 18)
bst.insert(root, 25)

# Search for an element
print("Is 18 present?", bool(bst.search(root, 18)))  # Output will be True

# In-order traversal
print("In-order Traversal:")
bst.inorder_traversal(root)  # Output will be 5 10 12 15 18 20 25

# Delete an element
root = bst.delete(root, 10)

# In-order traversal
print("In-order Traversal after deletion:")
bst.inorder_traversal(root)  # Output should be: 5 12 15 18 20 25

If you find this guide useful, please share with others, and spread the knowledge!

Leave a Comment

Your email address will not be published. Required fields are marked *