Deleting Node
Deleting a node in a Binary Search Tree (BST) requires careful handling of three distinct cases to maintain the BST property. Let’s understand the process visually first.
Visual representation of BST deletion casesLet’s visualize the decision-making process for node deletion:
flowchart LR Start["Node to Delete"] --> Check{"Node Found?"} Check -->|No| End["Return NULL"] Check -->|Yes| Children{"Child Count?"} Children -->|"Zero"| Case1["Case 1: Return NULL"] Children -->|"One"| Case2["Case 2: Replace with Child"] Children -->|"Two"| Case3["Case 3: Find Successor"] Case3 --> Successor["Copy Successor Value"] Successor --> Delete["Delete Successor"] classDef decision fill:#FFD700,stroke:#DAA520,color:#000 classDef action fill:#98FB98,stroke:#228B22,color:#000 classDef endpoint fill:#87CEEB,stroke:#4682B4,color:#000 class Check,Children decision class Case1,Case2,Case3,Successor,Delete action class Start,End endpoint
Let’s examine each case in detail:
Case 1: Deleting a Leaf Node (No Children)
- Simply remove the node and return NULL
- Example: Deleting node 2 in the first example above
- Maintains BST property automatically as no child relationships need to be preserved
Case 2: Deleting a Node with One Child
- Replace the node with its single child
- Example: Deleting node 3 in the second example above
- Child can be either left or right child
- Preserves BST property by maintaining the correct ordering of values
Case 3: Deleting a Node with Two Children
-
Most complex case requiring careful handling
-
Process:
- Find the successor (smallest value in right subtree)/ Find the predecessor (greatest value in left subtree)
- Copy successor’s value to the node being deleted
- Delete the successor node (which will be a leaf node or have one child)
-
Example: Deleting root node 5 in the third example above
Here’s a complete implementation in Python:
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def get_successor(node):
"""Find the smallest node in the right subtree"""
curr = node.right
while curr.left is not None:
curr = curr.left
return curr
def delete_node(root, key):
"""Delete a node with given key from BST"""
if root is None:
return root
# Search for the node to delete
if key < root.key:
root.left = delete_node(root.left, key)
elif key > root.key:
root.right = delete_node(root.right, key)
else:
# Case 1: No children
if root.left is None and root.right is None:
root = None
# Case 2: One child (right child)
elif root.left is None:
root = root.right
# Case 2: One child (left child)
elif root.right is None:
root = root.left
# Case 3: Two children
else:
successor = get_successor(root)
root.key = successor.key
root.right = delete_node(root.right, successor.key)
return rootTime and Space Complexity
-
Time Complexity: O(h) where h is the height of the tree
- Best case (balanced tree): O(\log n)
- Worst case (skewed tree): O(n)
-
Space Complexity:
- Recursive implementation: O(h) due to recursion stack
- Iterative implementation: O(1) extra space
Important Considerations
- Handle edge cases carefully:
- Empty tree
- Root node deletion
- Node not found in tree
- BST property must be maintained throughout the deletion process
- Choose recursive or iterative approach based on memory constraints:
- Recursive: Simpler code but higher memory usage
- Iterative: More complex code but constant extra space
This implementation ensures that the BST maintains its ordering property after deletion while handling all possible scenarios efficiently.