Implement Insertion, Search, and Deletion Operations for a Binary Search Tree (BST)
Problem Description
A Binary Search Tree (BST) is a special type of binary tree that satisfies the following properties:
- For each node in the tree, all values in its left subtree are less than the node's value.
- All values in its right subtree are greater than the node's value.
- The left and right subtrees are also binary search trees.
This problem requires you to implement three fundamental operations for a BST: inserting a new node, searching for a node with a specific value, and deleting a node with a specific value.
Basic Structure
First, we need to define the node structure for the BST:
class TreeNode:
def __init__(self, val=0):
self.val = val # Node value
self.left = None # Left child node
self.right = None # Right child node
Insert Operation
The goal of the insert operation is to add a new node to the BST while preserving the BST properties.
Step-by-step Breakdown:
- Base Case: If the tree is empty (root is None), directly create a new node as the root.
- Compare Values:
- If the value to insert is less than the current node's value, recursively insert into the left subtree.
- If the value to insert is greater than the current node's value, recursively insert into the right subtree.
- If the values are equal, handle it according to specific requirements (typically, BSTs do not allow duplicate values).
Detailed Process:
def insert(root, val):
# Base case: reached an empty position, create a new node
if root is None:
return TreeNode(val)
# Recursive insertion
if val < root.val:
root.left = insert(root.left, val) # Insert into left subtree
elif val > root.val:
root.right = insert(root.right, val) # Insert into right subtree
return root # Return the updated root node
Search Operation
The search operation looks for a node with a specific value in the BST.
Step-by-step Breakdown:
- Base Case 1: If the tree is empty or the target value is found, return the current node.
- Compare Values:
- If the target value is less than the current node's value, continue searching in the left subtree.
- If the target value is greater than the current node's value, continue searching in the right subtree.
Detailed Process:
def search(root, val):
# Base case: tree is empty or target found
if root is None or root.val == val:
return root
# Determine search direction based on value comparison
if val < root.val:
return search(root.left, val) # Search in left subtree
else:
return search(root.right, val) # Search in right subtree
Delete Operation
The delete operation is the most complex of the three, requiring handling of three different cases.
Three Deletion Cases:
- Node to delete is a leaf node: Delete it directly.
- Node to delete has only one child: Replace the deleted node with its child.
- Node to delete has two children: Find the minimum node in the right subtree (or the maximum node in the left subtree) to replace it.
Step-by-step Breakdown:
def delete(root, val):
if root is None:
return root # Tree is empty, return directly
# 1. Find the node to delete
if val < root.val:
root.left = delete(root.left, val) # Delete from left subtree
elif val > root.val:
root.right = delete(root.right, val) # Delete from right subtree
else:
# 2. Found the node to delete, handle the three cases
# Case 1: Node has only one child or no children
if root.left is None:
return root.right # Replace with right child
elif root.right is None:
return root.left # Replace with left child
# Case 2: Node has two children
# Find the minimum node in the right subtree (in-order successor)
temp = find_min(root.right)
# Replace current node's value with the successor's value
root.val = temp.val
# Delete the successor node from the right subtree (it has at most one right child now)
root.right = delete(root.right, temp.val)
return root
def find_min(node):
"""Find the minimum node in the subtree rooted at node"""
current = node
while current.left is not None:
current = current.left
return current
Operation Example
Taking the insertion sequence [5, 3, 7, 2, 4, 6, 8] as an example:
-
Insertion Process:
- Insert 5: Becomes the root node.
- Insert 3: 3 < 5, becomes the left child of 5.
- Insert 7: 7 > 5, becomes the right child of 5.
- Insert 2: 2 < 5 → 2 < 3, becomes the left child of 3.
- And so on...
-
Deleting Node 3 (which has two children):
- Find the minimum node in 3's right subtree (4).
- Replace 3 with 4.
- Delete the original 4 (this node has no left child).
Time Complexity Analysis
- Average Case: O(log n), when the tree is relatively balanced.
- Worst Case: O(n), when the tree degenerates into a linked list (e.g., inserting a sorted sequence).
- Insertion, search, and deletion all have the same time complexity, which depends on the height of the tree.
Key Points
- The properties of a BST ensure efficient searching.
- The delete operation is challenging, especially when handling nodes with two children.
- Maintaining BST balance is crucial; otherwise, performance degrades.
- In practical applications, balanced BSTs (such as AVL trees or Red-Black trees) are often used.