A B+ Tree is an efficient self-balancing data structure widely used in databases and file systems to manage large datasets. It extends the B-tree and stores all actual data in the leaf nodes, while the internal nodes only store keys used for navigation.
Deletion in a B+ Tree is more involved than insertion because it requires ensuring that the tree remains balanced after removing a key. This involves potentially redistributing or merging nodes if they underflow (i.e., have fewer keys than the minimum required). The deletion process also involves updating parent nodes to ensure that the tree structure remains valid.
Before diving into the deletion process, let’s quickly review the basic structure of a B+ Tree:
2*t - 1
keys, and leaf nodes can hold 2*t - 1
keys.t-1
keys), the node is underfull.Below is a Python implementation that demonstrates how to perform deletion in a B+ Tree. The code includes functions for searching, deleting, and rebalancing the tree by borrowing or merging nodes.
class BPlusTreeNode:
def __init__(self, t, leaf=False):
self.t = t # Minimum degree (defines range for the number of keys)
self.leaf = leaf # True if the node is a leaf node
self.keys = [] # List of keys in the node
self.children = [] # List of children nodes
self.next = None # For linking leaf nodes in a linked list
class BPlusTree:
def __init__(self, t):
self.t = t # Minimum degree
self.root = BPlusTreeNode(t, True) # Root node is initially a leaf node
def delete(self, key):
# Call delete on the root and adjust the tree if needed
self.delete_from_node(self.root, key)
if len(self.root.keys) == 0:
# If the root has no keys, set the root to its only child
if not self.root.leaf:
self.root = self.root.children[0]
def delete_from_node(self, node, key):
if node.leaf:
# If the key is in the leaf node, delete it directly
if key in node.keys:
node.keys.remove(key)
return
# If the key is not in the leaf, navigate through the internal node
i = 0
while i < len(node.keys) and key > node.keys[i]:
i += 1
child = node.children[i]
if len(child.keys) < self.t - 1:
# If the child is underfull, try to fix it by borrowing or merging
self.fix_underflow(node, i)
self.delete_from_node(node.children[i], key)
def fix_underflow(self, parent, index):
# Fix underflow in the child node by borrowing or merging
child = parent.children[index]
if index > 0 and len(parent.children[index - 1].keys) > self.t - 1:
# Borrow a key from the left sibling
left_sibling = parent.children[index - 1]
self.borrow_from_left(parent, index, left_sibling, child)
elif index < len(parent.children) - 1 and len(parent.children[index + 1].keys) > self.t - 1:
# Borrow a key from the right sibling
right_sibling = parent.children[index + 1]
self.borrow_from_right(parent, index, child, right_sibling)
else:
# Merge the node with a sibling
if index < len(parent.children) - 1:
# Merge with the right sibling
self.merge(parent, index, child, parent.children[index + 1])
else:
# Merge with the left sibling
self.merge(parent, index - 1, parent.children[index - 1], child)
def borrow_from_left(self, parent, index, left_sibling, child):
# Move a key from the left sibling to the parent, and move a key from the parent to the child
parent.keys[index - 1] = left_sibling.keys[-1]
child.keys.insert(0, parent.keys[index - 1])
left_sibling.keys.pop()
def borrow_from_right(self, parent, index, child, right_sibling):
# Move a key from the right sibling to the parent, and move a key from the parent to the child
parent.keys[index] = right_sibling.keys[0]
child.keys.append(parent.keys[index])
right_sibling.keys.pop(0)
def merge(self, parent, index, left_child, right_child):
# Merge left_child and right_child, and update the parent
left_child.keys.append(parent.keys[index])
left_child.keys.extend(right_child.keys)
parent.keys.pop(index)
parent.children.pop(index + 1)
def inorder_traversal(self):
# Perform an in-order traversal to print keys in sorted order
self.inorder_traversal_helper(self.root)
def inorder_traversal_helper(self, node):
if node:
i = 0
while i < len(node.keys):
if not node.leaf:
self.inorder_traversal_helper(node.children[i])
print(node.keys[i], end=" ")
i += 1
if not node.leaf:
self.inorder_traversal_helper(node.children[i])
# Example Usage
btree = BPlusTree(3) # Create a B+ tree with minimum degree 3
keys = [10, 20, 5, 6, 12, 30, 7, 17]
# Insert keys into the B+ Tree
for key in keys:
btree.insert(key)
# In-order Traversal (prints keys in sorted order)
print("In-order Traversal of B+ Tree before deletion:")
btree.inorder_traversal()
# Deleting a key from the B+ Tree
key_to_delete = 12
print(f"\nDeleting key {key_to_delete} from B+ Tree...")
btree.delete(key_to_delete)
# In-order Traversal after deletion (prints keys in sorted order)
print("In-order Traversal of B+ Tree after deletion:")
btree.inorder_traversal()
BPlusTreeNode Class:
next
pointer is used for linking leaf nodes in a doubly linked list.BPlusTree Class:
delete_from_node
to recursively navigate the tree and perform necessary operations.