A Red-Black Tree is a type of self-balancing binary search tree with an additional constraint that helps maintain balance during insertion and deletion operations. It ensures that the tree remains balanced by enforcing specific rules on the colors of nodes. These constraints help guarantee that the tree remains approximately balanced, leading to efficient search, insertion, and deletion operations.
In this blog post, we will cover the fundamental properties, operations, and the insertion process of a Red-Black Tree. We will also provide a Python code implementation for insertion into a Red-Black Tree to help you understand the mechanics.
A Red-Black Tree (RBT) is a binary search tree where each node has an extra bit of storage for its color, which can either be red or black. It is self-balancing and guarantees that the tree remains balanced after insertions and deletions, thus maintaining an efficient time complexity for operations like searching, insertion, and deletion.
A Red-Black Tree follows five key properties:
Node Colors:
Root Property:
Red Node Property:
Black Height Property:
Leaf Nodes:
The main advantage of Red-Black Trees over regular binary search trees is that they are guaranteed to be approximately balanced, ensuring that the operations of searching, inserting, and deleting have a worst-case time complexity of O(log n).
Insertion in a Red-Black Tree involves placing a new node in the tree, just like in a regular binary search tree, but with additional operations to maintain the Red-Black Tree properties.
Steps for insertion:
Deletion in a Red-Black Tree is more complex because it involves fixing the tree to maintain its properties. After removing the node, the tree might violate the Red-Black properties, so we use various cases of rotations and color flips to restore balance.
Insertion in a Red-Black Tree involves the following steps:
There are several cases to handle after insertion:
Below is a Python implementation of insertion in a Red-Black Tree. This code demonstrates how to insert a node while maintaining the Red-Black Tree properties using rotations and color flips.
class Node:
def __init__(self, data):
self.data = data
self.color = "RED" # New nodes are always inserted as red
self.left = None
self.right = None
self.parent = None
class RedBlackTree:
def __init__(self):
self.TNULL = Node(0) # Sentinel node for leaves
self.TNULL.color = "BLACK"
self.root = self.TNULL
def left_rotate(self, x):
y = x.right
x.right = y.left
if y.left != self.TNULL:
y.left.parent = x
y.parent = x.parent
if x.parent == self.TNULL:
self.root = y
elif x == x.parent.left:
x.parent.left = y
else:
x.parent.right = y
y.left = x
x.parent = y
def right_rotate(self, x):
y = x.left
x.left = y.right
if y.right != self.TNULL:
y.right.parent = x
y.parent = x.parent
if x.parent == self.TNULL:
self.root = y
elif x == x.parent.right:
x.parent.right = y
else:
x.parent.left = y
y.right = x
x.parent = y
def fix_insert(self, k):
while k.parent.color == "RED":
if k.parent == k.parent.parent.left:
u = k.parent.parent.right
if u.color == "RED":
u.color = "BLACK"
k.parent.color = "BLACK"
k.parent.parent.color = "RED"
k = k.parent.parent
else:
if k == k.parent.right:
k = k.parent
self.left_rotate(k)
k.parent.color = "BLACK"
k.parent.parent.color = "RED"
self.right_rotate(k.parent.parent)
else:
u = k.parent.parent.left
if u.color == "RED":
u.color = "BLACK"
k.parent.color = "BLACK"
k.parent.parent.color = "RED"
k = k.parent.parent
else:
if k == k.parent.left:
k = k.parent
self.right_rotate(k)
k.parent.color = "BLACK"
k.parent.parent.color = "RED"
self.left_rotate(k.parent.parent)
if k == self.root:
break
self.root.color = "BLACK"
def insert(self, key):
node = Node(key)
node.parent = None
node.data = key
node.left = self.TNULL
node.right = self.TNULL
node.color = "RED"
y = None
x = self.root
while x != self.TNULL:
y = x
if node.data < x.data:
x = x.left
else:
x = x.right
node.parent = y
if y == None:
self.root = node
elif node.data < y.data:
y.left = node
else:
y.right = node
if node.parent == None:
node.color = "BLACK"
return
if node.parent.parent == None:
return
self.fix_insert(node)
def inorder(self, root):
if root != self.TNULL:
self.inorder(root.left)
print(root.data, end=" ")
self.inorder(root.right)
# Example Usage
rb_tree = RedBlackTree()
# Insert keys into the Red-Black Tree
keys = [20, 15, 25, 10, 5, 1]
for key in keys:
rb_tree.insert(key)
# In-order traversal of the Red-Black Tree
print("In-order Traversal of Red-Black Tree:")
rb_tree.inorder(rb_tree.root)
Node Class:
data
field, a color
field (either "RED" or "BLACK"), and pointers to its left child, right child, and parent.TNULL
is used to represent leaves and simplifies boundary cases.RedBlackTree Class:
insert():
fix_insert()
to restore the Red-Black properties.inorder():