Open In App

Binary Search Tree In Python

Last Updated : 08 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

A Binary search tree is a binary tree where the values of the left sub-tree are less than the root node and the values of the right sub-tree are greater than the value of the root node. In this article, we will discuss the binary search tree in Python.

What is a Binary Search Tree(BST)?

A Binary Search Tree is a data structure used in computer science for organizing and storing data in a sorted manner. Each node in a Binary Search Tree has at most two children, a left child and a right child.

Properties of BST

  • The left side of a node only has smaller values.
  • The right side of a node only has bigger values.
  • Both the left and right sides follow the same rule, forming a smaller binary search tree.

Binary Search Tree

In this binary tree

  • Root node: 8
  • Left subtree (3): values < 8
  • Right subtree (10): values > 8
  • Each subtree maintains the same rule, with values relative to their respective roots.

Creation of Binary Search Tree (BST) in Python

Below, are the steps to create a Binary Search Tree (BST).

  • Create a class Tree
  • In the parameterized constructor, we have the default parameter val = None
  • When the Tree class is instantiated without any parameter then it creates an empty node with left and right pointers
  • If we pass the value while instantiating then it creates a node having that value and left, right pointers are created with None Types.
Python3
class Tree:
    def __init__(self,val=None):
        self.value = val
        if self.value:
            self.left = Tree()
            self.right = Tree()
        else:
            self.left = None
            self.right = None
#creating a node in the tree
t = Tree(5)

Time complexity: O(1)
Space complexity: O(1)

Basic Operations on Binary Search Tree (BST)

Below, are the some basic Operations of Binary Search Tree(BST) in Python.

Insertion in Binary Search Tree(BST) in Python

Inserting a node in a Binary search tree involves adding a new node to the tree while maintaining the binary search tree (BST) property. So we need to traverse through all the nodes till we find a leaf node and insert the node as the left or right child based on the value of that leaf node. So the three conditions that we need to check at the leaf node are listed below:

Illustrations

Let us insert 13 into the below Binary search tree


Untitled-Diagramdrawio-(1)

Binary Search Tree


  • When we call the insert method then 13 is checked with the root node which is 15 , Since 13 < 15 we need to insert 13 to the left sub tree. So we recursively call left_sub_tree.insert(13)
  • Now the left child of 15 is 10 so 13 is checked with 10 and since 13>10 we need to insert 13 to the right sub tree. So we recursively call right_sub_tree.insert(13)
  • Here, the right child of 10 is 11 so 13 is checked with 11 and since 13>11 and we need to insert 13 to the right child of 11. Again we recursively call right_sub_tree.insert(13)
  • In this recursion call, we found there is no node which means we reached till the leaf node, so we create a node and insert the data in the node

After inserting 13 , the BST looks like this :


Untitled-Diagramdrawio-(2)

Inserted 13 Node in Binary Search Tree


Insertion in Binary Search Tree (BST) in Python

Python3
class Tree:
    def __init__(self,val=None):
      
        # Initialize the Tree node with a value
        self.value = val
        
        # If the node has a value, 
        #create left and right children nodes
        if self.value:
            self.left = Tree()
            self.right = Tree()
        else:
          
            # If the node has no value, 
            #set left and right children to None
            self.left = None
            self.right = None
    
    # Check if the node is empty (has no value)
    def isempty(self):
        return (self.value == None)
    
    # Insert a new value into the tree
    def insert(self,data):
      
        # If the node is empty, insert the data here
        if self.isempty():
            self.value = data
            
            # Create left and right children 
            #for the inserted node
            self.left = Tree()
            self.right = Tree()
            print("{} is inserted successfully".format(self.value))
            
        # If data is less than current node value, 
        #insert into left subtree
        elif data < self.value:
            self.left.insert(data)
            return
          
        # If data is greater than current node value, 
        #insert into right subtree
        elif data > self.value:
            self.right.insert(data)
            
        # If data is equal to current node value, do nothing
        elif data == self.value:
            return

# Create a tree with root value 15
t = Tree(15)
# Insert some values into the tree
t.insert(10)
t.insert(18)
t.insert(4)
t.insert(11)
t.insert(16)
t.insert(20)
t.insert(13)

Output
10 is inserted successfully
18 is inserted successfully
4 is inserted successfully
11 is inserted successfully
16 is inserted successfully
20 is inserted successfully
13 is inserted successfully

Time complexity: O(log n)
Space complexity: O(n)

Searching in Binary Search Tree in Python

Searching in Binary Search Tree is very easy because we don’t need to traverse all the nodes. It is based on the value of nodes,which means if the value is less than root then we search in the left sub tree or else we search in right sub tree.

Illustrations

Steps to search a value ‘v’ in BST:

  • The value is checked with the root node, if it is equal to the root node then it returns the value
  • If the value is less than root node then it recursively calls the search operation in the left sub tree
  • If the value is greater than root node then it recursively calls the search operation in the right sub tree


Untitled-Diagramdrawio-(3)

Binary Search Tree


  • In above example BST search for value ‘8’ begins from the root node.
  • If the value is not equal to the current node’s value, the search continues recursively on the left subtree if the value is less than the current node’s value.
  • Once the value matches a node, the search terminates, returning the node containing the value.

Searching in Binary Search Tree (BST) in Python

Python3
class Tree:
    def __init__(self, val=None):
        # Initialize a tree node with a value
        self.value = val
        if self.value:
            # If a value is provided, 
            #create left and right children as empty trees
            self.left = Tree()
            self.right = Tree()
        else:
            # If no value is provided, set left and 
            #right children to None
            self.left = None
            self.right = None
    
    def isempty(self):
        # Check if the tree node is empty
        return self.value == None
    
    def isleaf(self):
        # Check if the tree node is a leaf node (both left and right children are None)
        if self.left.left == None and self.right.right == None:
            return True
        else:
            return False
    
    def insert(self, data):
        if self.isempty():
            # If the current node is empty, 
            #insert the data as its value
            self.value = data
            # Create empty left and right children
            self.left = Tree()
            self.right = Tree()
        elif self.value == data:
            # If the data already exists in the tree, return
            return
        elif data < self.value:
            # If the data is less than the current node's value, 
            #insert it into the left subtree
            self.left.insert(data)
            return
        elif data > self.value:
            # If the data is greater than the current node's value, 
            #insert it into the right subtree
            self.right.insert(data)
            return
    
    def find(self, v):
        if self.isempty():
            # If the tree is empty, the value is not found
            print("{} is not found".format(v))
            return False
        if self.value == v:
            # If the value is found at the current node, 
            #print a message and return True
            print("{} is found".format(v))
            return True
        if v < self.value:
            # If the value is less than the current node's value, 
            #search in the left subtree
            return self.left.find(v)
        else:
            # If the value is greater than the current node's value, 
            #search in the right subtree
            return self.right.find(v)
    
    def inorder(self):
        if self.isempty():
            # If the tree is empty, return an empty list
            return []
        else:
            # Return the inorder traversal of the tree (left subtree, root, right subtree)
            return self.left.inorder() + [self.value] + self.right.inorder()

# Example usage
t = Tree(20)
t.insert(15)
t.insert(25)
t.insert(8)
t.insert(16)
t.find(8)
print(t.inorder())

Output
8 is found
[8, 15, 16, 20, 25]

Time complexity: O(log n)
Space complexity: O(n)

Deletion in Binary Search Tree in Python

Deleting a node in Binary search tree involves deleting an existing node while maintaining the properties of Binary Search Tree(BST). we need to search the node which needs to be deleted and then delete it.

Case 1 : While deleting a node having both left child and right child

  • After finding the node to be deleted, copy the value of right child to v and copy the right child’s left pointer to left of v and right pointer


Untitled-Diagramdrawio-(6)

Delete node 18 in BST


  • In the above example, suppose we need to delete 18 then we need to replace 18 with the maximum value of its left or right sub tree
  • Let us replace it with 20 which is maximum value of right sub tree
  • So after deleting 18, the Binary search tree looks like this
Testdrawio-(1)

Case 2 : While deleting a node having either left child or right child

  • After finding the node to be deleted, we need to replace the value in the node with its left node it has left child or right node if it has right child.


Untitled-Diagramdrawio-(8)

Delete 16 in BST


  • In the above example, suppose we need to delete 16 then we need to replace 16 with the value of its right child node
  • So it will be replaced with 17 which is the value of its right child node
  • So after deleting 16, the Binary search tree looks like this


Untitled-Diagram-(4)

Deleted 16 in BST


Case 3 : While deleting a leaf node(a node which has no child)


Untitled-Diagramdrawio-(9)

Delete 4 in BST

  • In the above example, suppose we need to delete 4 which is a leaf node.
  • Simply we change value of the node, left and right pointers to None.
  • After deleting 4 the BST looks like this:
Untitled-Diagram-(6)

Deleted 4 in BST

Deletion in Binary Search Tree in Python

Python3
class Tree:
    def __init__(self, val=None):
        # Initialize the Tree node with a value
        self.value = val
        # Create left and right child nodes 
        #if the value is not None
        if self.value:
            self.left = Tree()
            self.right = Tree()
        else:
            self.left = None
            self.right = None

    def isempty(self):
        # Check if the node is empty (value is None)
        return (self.value == None)

    def insert(self, data):
        # Insert a new value into the Tree
        if self.isempty():
            self.value = data
            self.left = Tree()
            self.right = Tree()
        elif self.value == data:
            return
        elif data < self.value:
            self.left.insert(data)
            return
        elif data > self.value:
            self.right.insert(data)
            return

    def isleaf(self):
        # Check if the node is a leaf node (no children)
        if self.left == None and self.right == None:
            return True
        else:
            return False

    def maxval(self):
        # Find the maximum value in the Tree
        if self.right.right == None:
            return (self.value)
        else:
            return (self.right.maxval())

    def delete(self, v):
        # Delete a value from the Tree
        if self.isempty():
            return
        if v < self.value:
            self.left.delete(v)
            return
        if v > self.value:
            self.right.delete(v)
            return
        if v == self.value:
            if self.isleaf():
                self.value = None
                self.left = None
                self.right = None
                return
            elif self.left.isempty():
                self.value = self.right.value
                self.left = self.right.left
                self.right = self.right.right
                return
            else:
                self.value = self.left.maxval()
                self.left.delete(self.left.maxval())
                return

    def inorder(self):
        # Traverse the Tree in inorder (left-root-right) 
        #and return the values
        if self.isempty():
            return ([])
        else:
            return (self.left.inorder() + [self.value] + self.right.inorder())

# Test the Tree class
t = Tree(15)
t.insert(10)
t.insert(18)
t.insert(4)
t.insert(11)
t.insert(16)
t.insert(20)
print("Before deleting 4: ")
print(t.inorder())
t.delete(4)
print("After deleting 4: ")
print(t.inorder())

Output
Before deleting 4: 
[4, 10, 11, 15, 16, 18, 20]
After deleting 4: 
[10, 11, 15, 16, 18, 20]

Time complexity: O(log n)
Space complexity: O(1)

Traversals in Binary Search Tree in Python

Below, are the traversals of Binary Search Tree (BST) in Python which we can perform as SImilar to Binary Tree.

Traversals in BST in Python

Python3
class Tree:
    def __init__(self, val=None):
        # Initialize the node with a value and left and right subtrees
        self.value = val
        if self.value:
            self.left = Tree()
            self.right = Tree()
        else:
            self.left = None
            self.right = None
    
    def isempty(self):
        # Check if the tree is empty
        return self.value is None
    
    def insert(self, data):
        # Insert a value into the tree
        if self.isempty():
            self.value = data
            self.left = Tree()
            self.right = Tree()
        elif self.value == data:
            return
        elif data < self.value:
            self.left.insert(data)
        elif data > self.value:
            self.right.insert(data)
    
    def isleaf(self):
        # Check if the node is a leaf node
        return self.left is None and self.right is None
    
    def inorder(self):
        # Perform inorder traversal
        if self.isempty():
            return []
        else:
            return self.left.inorder() + [self.value] + self.right.inorder()

# Example usage
t = Tree(10)
t.insert(8)
t.insert(6)
t.insert(12)

print("Inorder Traversal : ")
print(t.inorder())

Output
Inorder Traversal : 
[6, 8, 10, 12]

Time complexity: O(log n)
Space complexity: O(n)

Applications of BST:

  • Graph algorithms: BSTs can be used to implement graph algorithms, such as in minimum spanning tree algorithms.
  • Priority Queues: BSTs can be used to implement priority queues, where the element with the highest priority is at the root of the tree, and elements with lower priority are stored in the subtrees.
  • Self-balancing binary search tree: BSTs can be used as a self-balancing data structures such as AVL tree and Red-black tree.
  • Data storage and retrieval: BSTs can be used to store and retrieve data quickly, such as in databases, where searching for a specific record can be done in logarithmic time.

Advantages:

  • Fast search: Searching for a specific value in a BST has an average time complexity of O(log n), where n is the number of nodes in the tree. This is much faster than searching for an element in an array or linked list, which have a time complexity of O(n) in the worst case.
  • In-order traversal: BSTs can be traversed in-order, which visits the left subtree, the root, and the right subtree. This can be used to sort a dataset.
  • Space efficient: BSTs are space efficient as they do not store any redundant information, unlike arrays and linked lists.

Disadvantages:

  • Skewed trees: If a tree becomes skewed, the time complexity of search, insertion, and deletion operations will be O(n) instead of O(log n), which can make the tree inefficient.
  • Additional time required: Self-balancing trees require additional time to maintain balance during insertion and deletion operations.
  •  Efficiency: BSTs are not efficient for datasets with many duplicates as they will waste space.




Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads