Table of contents
1.
Introduction
1.1.
Prerequisites
2.
Algorithm:
3.
Example
4.
C++ Implementation
4.1.
Output
5.
Time Complexity: O(logN)
6.
Space Complexity: O(1)
7.
FAQs
8.
Key Takeaways
Last Updated: Mar 27, 2024

Red-Black Tree (Delete)

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Prerequisites

  1. Red-Black Tree
  2. Insertion in Red-Black Tree

 

The first step to delete a node in the Red-Black tree is similar to the deletion operation in Binary Search Trees. After deleting, some of the Red-Black Tree properties may be violated. We then use rotations and recoloring to maintain the properties of the Red-Black tree. 

In insertion, we checked the color of the uncle of the new node to decide the appropriate case. To delete a node in the Red-Black tree, we check the color of the node's sibling to be deleted to decide the appropriate case.

The following are the properties of a Red-Black Tree:

  1. Every node has either a red or black color.
  2. The root is always black.
  3. No two red nodes can be adjacent.
  4. Every path from the root to a leaf has the same number of black nodes.

 

The main property that can be violated when we delete a node in the Red-Black tree is that if we delete a black node, the number of black nodes in the path from the root to the leaf can decrease. Deletion is complex compared to insertion. 

Double Black Node: To delete a node in the Red-Black tree, we introduce the concept of the double black node. When the deleted node is black, and a black child replaces it, we mark that child as double black.

Algorithm:

  1. Perform the simple Binary Search Tree delete operation.
  2. Let ‘v’ be the node to be deleted and ‘u’ be the child that replaces ‘v’. Note that both ‘v’ and ‘u’ can’t be Red as it will violate the Red-Black tree properties.
  3. Case 1 (Simple): If either ‘v’ or ‘u’ is red.
    1. Mark ‘u’ as black. This way black height will not change.

                                       Source

4. Case 2: If both ‘u’ and ‘v’ are black.

  1. Color the node ‘u’ as double black. Now our task is to convert this double black node to single black. Let ‘s’ be the sibling of ‘u’.
  2. Case 1: ‘s’ is black and has at least one red child.
    We can divide this case into four subcases:
    (i) Left left case: (s is the left sibling of u, and the left child of s is red).
    (ii) Left right case: (s is the left sibling of u, and the right child of s is red).
    (iii) Right right case: (s is the right sibling of u, and the right child of s is red).

    (iv) Right left case: (s is the right sibling of u, and the left child of s is red).

                                Source
  3. Case 2: If ‘s’ is black, and both its children are black.
    If the sibling and its children are black, we perform the recoloring.
    (i) If the parent is red, we can simple make both the nodes black (red + double black = single black).

    (ii) Else, the parent becomes double black and we continue this process upwards until double black node is removed.

                                  Source
  4. Case 3: If the sibling is red.
    In this case, we perform the rotation to move the old sibling up and recolor the parent and old sibling. The new sibling will always be black as two adjacent nodes can’t be red.

Example

The following example illustrates the steps on how to delete a node in the Red-Black tree.

Both the children of 9 are black, so we introduce a double black node.

Since the parent of the double black node was red, we recolor both of them as black.

Now, delete 8 from the above tree. The left child of 8 is red, so we will simply replace 8 with 7 and color it black.

Now, delete 7 from the above tree. Both the children of 7 are black, so we introduce a double black node. The left sibling of 7 is black and has red children, therefore we perform the rotation operation of left left case.

                         Source

C++ Implementation

#include <bits/stdc++.h>
using namespace std;

enum Color
{
    RED,
    BLACK,
    DOUBLE_BLACK
};

struct Node
{
    int data;
    int color;
    Node *left, *right, *parent;

    explicit Node(int);
};

class RBTree
{
private:
    Node *root;

protected:
    void rotateLeft(Node *&);
    void rotateRight(Node *&);
    void fixInsertRBTree(Node *&);
    void fixDeleteRBTree(Node *&);
    void inorderBST(Node *&);
    void preorderBST(Node *&);
    int getColor(Node *&);
    void setColor(Node *&, int);
    Node *minValueNode(Node *&);
    Node *maxValueNode(Node *&);
    Node *insertBST(Node *&, Node *&);
    Node *deleteBST(Node *&, int);
    int getBlackHeight(Node *);

public:
    RBTree();
    void insertValue(int);
    void deleteValue(int);
    void merge(RBTree);
    void inorder();
    void preorder();
};

Node::Node(int data)
{
    this->data = data;
    color = RED;
    left = right = parent = nullptr;
}

RBTree::RBTree()
{
    root = nullptr;
}

int RBTree::getColor(Node *&node)
{
    if (node == nullptr)
        return BLACK;

    return node->color;
}

void RBTree::setColor(Node *&node, int color)
{
    if (node == nullptr)
        return;

    node->color = color;
}

Node *RBTree::insertBST(Node *&root, Node *&ptr)
{
    if (root == nullptr)
        return ptr;

    if (ptr->data < root->data)
    {
        root->left = insertBST(root->left, ptr);
        root->left->parent = root;
    }
    else if (ptr->data > root->data)
    {
        root->right = insertBST(root->right, ptr);
        root->right->parent = root;
    }

    return root;
}

void RBTree::insertValue(int n)
{
    Node *node = new Node(n);
    root = insertBST(root, node);
    fixInsertRBTree(node);
}

void RBTree::rotateLeft(Node *&ptr)
{
    Node *right_child = ptr->right;
    ptr->right = right_child->left;

    if (ptr->right != nullptr)
        ptr->right->parent = ptr;

    right_child->parent = ptr->parent;

    if (ptr->parent == nullptr)
        root = right_child;
    else if (ptr == ptr->parent->left)
        ptr->parent->left = right_child;
    else
        ptr->parent->right = right_child;

    right_child->left = ptr;
    ptr->parent = right_child;
}

void RBTree::rotateRight(Node *&ptr)
{
    Node *left_child = ptr->left;
    ptr->left = left_child->right;

    if (ptr->left != nullptr)
        ptr->left->parent = ptr;

    left_child->parent = ptr->parent;

    if (ptr->parent == nullptr)
        root = left_child;
    else if (ptr == ptr->parent->left)
        ptr->parent->left = left_child;
    else
        ptr->parent->right = left_child;

    left_child->right = ptr;
    ptr->parent = left_child;
}

void RBTree::fixInsertRBTree(Node *&ptr)
{
    Node *parent = nullptr;
    Node *grandparent = nullptr;
    while (ptr != root && getColor(ptr) == RED && getColor(ptr->parent) == RED)
    {
        parent = ptr->parent;
        grandparent = parent->parent;
        if (parent == grandparent->left)
        {
            Node *uncle = grandparent->right;
            if (getColor(uncle) == RED)
            {
                setColor(uncle, BLACK);
                setColor(parent, BLACK);
                setColor(grandparent, RED);
                ptr = grandparent;
            }
            else
            {
                if (ptr == parent->right)
                {
                    rotateLeft(parent);
                    ptr = parent;
                    parent = ptr->parent;
                }
                rotateRight(grandparent);
                swap(parent->color, grandparent->color);
                ptr = parent;
            }
        }
        else
        {
            Node *uncle = grandparent->left;
            if (getColor(uncle) == RED)
            {
                setColor(uncle, BLACK);
                setColor(parent, BLACK);
                setColor(grandparent, RED);
                ptr = grandparent;
            }
            else
            {
                if (ptr == parent->left)
                {
                    rotateRight(parent);
                    ptr = parent;
                    parent = ptr->parent;
                }
                rotateLeft(grandparent);
                swap(parent->color, grandparent->color);
                ptr = parent;
            }
        }
    }
    setColor(root, BLACK);
}

// Function to delete a node in the Red-Black tree
void RBTree::fixDeleteRBTree(Node *&node)
{
    if (node == nullptr)
        return;

    if (node == root)
    {
        root = nullptr;
        return;
    }

    if (getColor(node) == RED || getColor(node->left) == RED || getColor(node->right) == RED)
    {
        Node *child = node->left != nullptr ? node->left : node->right;

        if (node == node->parent->left)
        {
            node->parent->left = child;
            if (child != nullptr)
                child->parent = node->parent;
            setColor(child, BLACK);
            delete (node);
        }
        else
        {
            node->parent->right = child;
            if (child != nullptr)
                child->parent = node->parent;
            setColor(child, BLACK);
            delete (node);
        }
    }
    else
    {
        Node *sibling = nullptr;
        Node *parent = nullptr;
        Node *ptr = node;
        setColor(ptr, DOUBLE_BLACK);
        while (ptr != root && getColor(ptr) == DOUBLE_BLACK)
        {
            parent = ptr->parent;
            if (ptr == parent->left)
            {
                sibling = parent->right;
                if (getColor(sibling) == RED)
                {
                    setColor(sibling, BLACK);
                    setColor(parent, RED);
                    rotateLeft(parent);
                }
                else
                {
                    if (getColor(sibling->left) == BLACK && getColor(sibling->right) == BLACK)
                    {
                        setColor(sibling, RED);
                        if (getColor(parent) == RED)
                            setColor(parent, BLACK);
                        else
                            setColor(parent, DOUBLE_BLACK);
                        ptr = parent;
                    }
                    else
                    {
                        if (getColor(sibling->right) == BLACK)
                        {
                            setColor(sibling->left, BLACK);
                            setColor(sibling, RED);
                            rotateRight(sibling);
                            sibling = parent->right;
                        }
                        setColor(sibling, parent->color);
                        setColor(parent, BLACK);
                        setColor(sibling->right, BLACK);
                        rotateLeft(parent);
                        break;
                    }
                }
            }
            else
            {
                sibling = parent->left;
                if (getColor(sibling) == RED)
                {
                    setColor(sibling, BLACK);
                    setColor(parent, RED);
                    rotateRight(parent);
                }
                else
                {
                    if (getColor(sibling->left) == BLACK && getColor(sibling->right) == BLACK)
                    {
                        setColor(sibling, RED);
                        if (getColor(parent) == RED)
                            setColor(parent, BLACK);
                        else
                            setColor(parent, DOUBLE_BLACK);
                        ptr = parent;
                    }
                    else
                    {
                        if (getColor(sibling->left) == BLACK)
                        {
                            setColor(sibling->right, BLACK);
                            setColor(sibling, RED);
                            rotateLeft(sibling);
                            sibling = parent->left;
                        }
                        setColor(sibling, parent->color);
                        setColor(parent, BLACK);
                        setColor(sibling->left, BLACK);
                        rotateRight(parent);
                        break;
                    }
                }
            }
        }
        if (node == node->parent->left)
            node->parent->left = nullptr;
        else
            node->parent->right = nullptr;
        delete (node);
        setColor(root, BLACK);
    }
}

Node *RBTree::deleteBST(Node *&root, int data)
{
    if (root == nullptr)
        return root;

    if (data < root->data)
        return deleteBST(root->left, data);

    if (data > root->data)
        return deleteBST(root->right, data);

    if (root->left == nullptr || root->right == nullptr)
        return root;

    Node *temp = minValueNode(root->right);
    root->data = temp->data;
    return deleteBST(root->right, temp->data);
}

// Function to delete a node in the Red-Black tree by value.
void RBTree::deleteValue(int data)
{
    Node *node = deleteBST(root, data);
    fixDeleteRBTree(node);
}

void RBTree::inorderBST(Node *&ptr)
{
    if (ptr == nullptr)
        return;

    inorderBST(ptr->left);
    cout << ptr->data << " " << ptr->color << endl;
    inorderBST(ptr->right);
}

void RBTree::inorder()
{
    inorderBST(root);
}

void RBTree::preorderBST(Node *&ptr)
{
    if (ptr == nullptr)
        return;

    cout << ptr->data << " " << ptr->color << endl;
    preorderBST(ptr->left);
    preorderBST(ptr->right);
}

void RBTree::preorder()
{
    preorderBST(root);
    cout << "-------" << endl;
}

Node *RBTree::minValueNode(Node *&node)
{

    Node *ptr = node;

    while (ptr->left != nullptr)
        ptr = ptr->left;

    return ptr;
}

Node *RBTree::maxValueNode(Node *&node)
{
    Node *ptr = node;

    while (ptr->right != nullptr)
        ptr = ptr->right;

    return ptr;
}

int RBTree::getBlackHeight(Node *node)
{
    int blackheight = 0;
    while (node != nullptr)
    {
        if (getColor(node) == BLACK)
            blackheight++;
        node = node->left;
    }
    return blackheight;
}

// Test case 1 : 5 2 9 1 6 8 0 20 30 35 40 50 0
// Test case 2 : 3 0 5 0
// Test case 3 : 2 1 3 0 8 9 4 5 0
void RBTree::merge(RBTree rbTree2)
{
    int temp;
    Node *c, *temp_ptr;
    Node *root1 = root;
    Node *root2 = rbTree2.root;
    int initialblackheight1 = getBlackHeight(root1);
    int initialblackheight2 = getBlackHeight(root2);
    if (initialblackheight1 > initialblackheight2)
    {
        c = maxValueNode(root1);
        temp = c->data;
        deleteValue(c->data);
        root1 = root;
    }
    else if (initialblackheight2 > initialblackheight1)
    {
        c = minValueNode(root2);
        temp = c->data;
        rbTree2.deleteValue(c->data);
        root2 = rbTree2.root;
    }
    else
    {
        c = minValueNode(root2);
        temp = c->data;
        rbTree2.deleteValue(c->data);
        root2 = rbTree2.root;
        if (initialblackheight1 != getBlackHeight(root2))
        {
            rbTree2.insertValue(c->data);
            root2 = rbTree2.root;
            c = maxValueNode(root1);
            temp = c->data;
            deleteValue(c->data);
            root1 = root;
        }
    }
    setColor(c, RED);
    int finalblackheight1 = getBlackHeight(root1);
    int finalblackheight2 = getBlackHeight(root2);
    if (finalblackheight1 == finalblackheight2)
    {
        c->left = root1;
        root1->parent = c;
        c->right = root2;
        root2->parent = c;
        setColor(c, BLACK);
        c->data = temp;
        root = c;
    }
    else if (finalblackheight2 > finalblackheight1)
    {
        Node *ptr = root2;
        while (finalblackheight1 != getBlackHeight(ptr))
        {
            temp_ptr = ptr;
            ptr = ptr->left;
        }
        Node *ptr_parent;
        if (ptr == nullptr)
            ptr_parent = temp_ptr;
        else
            ptr_parent = ptr->parent;
        c->left = root1;
        if (root1 != nullptr)
            root1->parent = c;
        c->right = ptr;
        if (ptr != nullptr)
            ptr->parent = c;
        ptr_parent->left = c;
        c->parent = ptr_parent;
        if (getColor(ptr_parent) == RED)
        {
            fixInsertRBTree(c);
        }
        else if (getColor(ptr) == RED)
        {
            fixInsertRBTree(ptr);
        }
        c->data = temp;
        root = root2;
    }
    else
    {
        Node *ptr = root1;
        while (finalblackheight2 != getBlackHeight(ptr))
        {
            ptr = ptr->right;
        }
        Node *ptr_parent = ptr->parent;
        c->right = root2;
        root2->parent = c;
        c->left = ptr;
        ptr->parent = c;
        ptr_parent->right = c;
        c->parent = ptr_parent;
        if (getColor(ptr_parent) == RED)
        {
            fixInsertRBTree(c);
        }
        else if (getColor(ptr) == RED)
        {
            fixInsertRBTree(ptr);
        }
        c->data = temp;
        root = root1;
    }
    return;
}

int main()
{
    int data;
    RBTree rbTree, rbTree2;
    rbTree.insertValue(7);
    rbTree.insertValue(3);
    rbTree.insertValue(18);
    rbTree.insertValue(10);
    rbTree.insertValue(22);
    rbTree.insertValue(8);
    rbTree.insertValue(11);
    rbTree.insertValue(26);
    rbTree.insertValue(2);
    rbTree.insertValue(6);
    rbTree.insertValue(13);

    rbTree.preorder();

    rbTree.deleteValue(18);
    rbTree.deleteValue(11);
    rbTree.deleteValue(3);
    rbTree.deleteValue(10);
    rbTree.deleteValue(22);

    rbTree.preorder();

    return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Output

10 1
7 0
3 1
2 0
6 0
8 1
18 0
11 1
13 0
22 1
26 0
-------
13 1
7 0
6 1
2 0
8 1
26 1
-------

Time Complexity: O(logN)

Every path from the root to the leaf has same number of black nodes. When we delete a node in the Red-Black tree, either a double-black node is introduced or recoloring happens. Recoloring takes O(1) time. To remove a double-black node, we either rotate and recolor, which takes O(1) time or, propagate the double-black node upwards. Propagating the double black node will take O(height) time, and the height of the Red-Black tree is of order O(logN). 

Therefore, the total time complexity to delete a node in the Red-Black tree is O(logN).

Space Complexity: O(1)

The deletion step searches for the required node and deletes it. To maintain the properties of the Red-Black tree, recoloring and rotations are used. These are implemented just by using a few variables. Therefore the space complexity of the approach is O(1).

FAQs

  1. What is the Red-Black tree?
    A Red-Black tree is a self-balancing Binary Search Tree having the following properties:
    Every node has either a red or black color.
    The root is always black.
    No two red nodes can be adjacent.
    Every path from the root to a leaf has the same number of black nodes.
     
  2. What is the height of a Red-Black tree with N nodes?
    A red-black tree with N nodes has height <= 2*log2(N+1).

Key Takeaways

We learned how to delete a node in the Red-Black tree. We discussed the rotations and recoloring operations that can be performed to maintain its properties. If you want to solve more such problems, you can visit Coding Ninjas Studio.

If you think that this blog helped you share it with your friends!. To be more confident in data structures and algorithms, try out our DS and Algo Course.

Check out this problem - Duplicate Subtree In Binary Tree

Until then, All the best for your future endeavours, and Keep Coding.

Live masterclass