Red-Black Tree

Red-Black Tree

A Red-Black Tree is a self-balancing binary search tree where each node has an extra bit for denoting the color of the node, either red or black. The tree is balanced through rotations and color changes during insertion and deletion operations.

Space Complexity: O(n)
Time Complexity: O(log n) for search, insert, and delete operations
Best Case: Ω(log n)
Average Case: Θ(log n)
Worst Case: O(log n)


class Color:
    RED = 0
    BLACK = 1

class Node:
    def __init__(self, data):
        self.data = data
        self.color = Color.RED
        self.left = None
        self.right = None
        self.parent = None

class RedBlackTree:
    def __init__(self):
        self.root = None

    def insert(self, data):
        new_node = Node(data)
        if not self.root:
            self.root = new_node
            self.root.color = Color.BLACK
        else:
            self._insert(self.root, new_node)
            self._fix_violation(new_node)

    def _insert(self, root, node):
        if not root:
            return node

        if node.data < root.data:
            root.left = self._insert(root.left, node)
            root.left.parent = root
        elif node.data > root.data:
            root.right = self._insert(root.right, node)
            root.right.parent = root

        return root

    def _rotate_left(self, node):
        temp_right = node.right
        node.right = temp_right.left
        if node.right:
            node.right.parent = node

        temp_right.parent = node.parent
        if not node.parent:
            self.root = temp_right
        elif node == node.parent.left:
            node.parent.left = temp_right
        else:
            node.parent.right = temp_right

        temp_right.left = node
        node.parent = temp_right

    def _rotate_right(self, node):
        temp_left = node.left
        node.left = temp_left.right
        if node.left:
            node.left.parent = node

        temp_left.parent = node.parent
        if not node.parent:
            self.root = temp_left
        elif node == node.parent.left:
            node.parent.left = temp_left
        else:
            node.parent.right = temp_left

        temp_left.right = node
        node.parent = temp_left

    def _fix_violation(self, node):
        while node != self.root and node.parent and node.parent.color == Color.RED:
            if node.parent.parent:
                if node.parent == node.parent.parent.left:
                    uncle = node.parent.parent.right
                    if uncle and uncle.color == Color.RED:
                        node.parent.color = Color.BLACK
                        uncle.color = Color.BLACK
                        node.parent.parent.color = Color.RED
                        node = node.parent.parent
                    else:
                        if node == node.parent.right:
                            node = node.parent
                            self._rotate_left(node)
                        node.parent.color = Color.BLACK
                        if node.parent.parent:
                            node.parent.parent.color = Color.RED
                            self._rotate_right(node.parent.parent)
                else:
                    uncle = node.parent.parent.left
                    if uncle and uncle.color == Color.RED:
                        node.parent.color = Color.BLACK
                        uncle.color = Color.BLACK
                        node.parent.parent.color = Color.RED
                        node = node.parent.parent
                    else:
                        if node == node.parent.left:
                            node = node.parent
                            self._rotate_right(node)
                        node.parent.color = Color.BLACK
                        if node.parent.parent:
                            node.parent.parent.color = Color.RED
                            self._rotate_left(node.parent.parent)
        if self.root:
            self.root.color = Color.BLACK

    def inorder(self, node):
        result = []
        if node:
            result = self.inorder(node.left)
            result.append(node.data)
            result = result + self.inorder(node.right)
        return result

    def preorder(self, node):
        result = []
        if node:
            result.append(node.data)
            result = result + self.preorder(node.left)
            result = result + self.preorder(node.right)
        return result

    def postorder(self, node):
        result = []
        if node:
            result = self.postorder(node.left)
            result = result + self.postorder(node.right)
            result.append(node.data)
        return result

    def display_traversal(self, traversal_type):
        if traversal_type == 'inorder':
            return ' -> '.join(map(str, self.inorder(self.root)))
        elif traversal_type == 'preorder':
            return ' -> '.join(map(str, self.preorder(self.root)))
        elif traversal_type == 'postorder':
            return ' -> '.join(map(str, self.postorder(self.root)))

# Example Usage
rb_tree = RedBlackTree()
for i in range(20):
    rb_tree.insert(random.randint(0, 100))

print("Inorder:", rb_tree.display_traversal('inorder'))
print("Preorder:", rb_tree.display_traversal('preorder'))
print("Postorder:", rb_tree.display_traversal('postorder'))