Red Black Tree (RB-Tree) Using C++
Last Updated :
17 Jul, 2024
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. This color coding is used to ensure that the tree remains balanced during insertions and deletions. In this article, we will learn how to implement a Red-Black Tree in C++ along with its basic operations.
A Red Black Tree is a type of self-balancing binary search tree. It maintains a near-perfect balance by coloring each node-red or black and enforcing a set of properties that guarantee that the deepest path in the tree is no more than twice as long as the shallowest path.
Properties of Red-Black Trees
- Every node is either red or black.
- The root node is always black.
- Every leaf (NIL or null node) is black.
- If a node is red, then both its children are black.
- For each node, all simple paths from the node to descendant leaves contain the same number of black nodes.
Implementation of Red Black Tree in C++
A Red Black Tree can be implemented as an extension of a binary search tree. Each node in the tree will have an extra attribute to store its color.
Representation of Red Black Tree in C++
To represent a Red Black Tree in C++, we'll use a struct Node to represent each node of the red-black tree and a class RedBlackTree that will contain all of the member functions. We have used the template to keep the red-black tree generic so that it can support multiple data types.
enum Color { RED, BLACK };
template <typename T>
class RedBlackTree {
private:
struct Node {
T data;
Color color;
Node *left;
Node*right;
Node*parent;
};
Here,
- data: stores the value in the node.
- color: an enum that stores the color of the node (RED or BLACK).
- left, right, and parent: store pointers to the left child, right child, and parent nodes respectively.
The following diagram represents the structure of red black tree:
Red-Black-Tree DiagramBasic Operations of Red Black Tree in C++
Following are some of the basic operations of a Red Black Tree that are required to manipulate its elements:
Operation | Description | Time Complexity | Space Complexity |
---|
Insert | Inserts a new element into the tree. | O(log n) | O(1) |
---|
Delete node | Removes an element from the tree. | O(log n) | O(1) |
---|
Search | Searches for an element in the tree. | O(log n) | O(1) |
---|
Left Rotate | Performs a left rotation on a given node. | O(1) | O(1) |
---|
Right Rotate | Performs a right rotation on a given node. | O(1) | O(1) |
---|
Here, n represents the number of nodes in the red black tree.
- Perform standard BST insertion and color the newly inserted node red.
- If the newly inserted node is the root, color it black and return.
- Do the following while the parent of the newly inserted node is red:
- If the parent is the left child of the grandparent:
- If the right child of the grandparent is red, recolor and move up the tree.
- Else, perform rotations and recolor.
- If the parent is the right child of the grandparent:
- If the left child of the grandparent is red, recolor and move up the tree.
- Else, perform rotations and recolor.
- Color the root black.
- Perform standard BST deletion.
- If the deleted node was red, we're done.
- If the deleted node was black, we need to fix the double black violation:
- If the sibling is red, perform rotations and recolor.
- If the sibling is black with both black children, recolor and move up the tree.
- If the sibling is black with at least one red child, perform rotations and recolor.
Implementation of Search Function
- Start from the root node.
- Compare the value to be searched with the value of the current node.
- If the value is less than the current node, move to the left child.
- If the value is greater than the current node, move to the right child.
- If the value is equal to the current node, return true.
- If a leaf node is reached and the value is not found, return false.
Implementation of Rotation Functions
- Left Rotation:
- Make the right child of the current node the new root of the subtree.
- Make the old root the left child of the new root.
- Update the parent pointers accordingly.
- Right Rotation:
- Make the left child of the current node the new root of the subtree.
- Make the old root the right child of the new root.
- Update the parent pointers accordingly.
C++ Program to Implement Red Black Tree
The following program demonstrates the implementation of a Red Black Tree in C++:
C++
// C++ Program to Implement Red Black Tree
#include <iostream>
using namespace std;
// Enumeration for colors of nodes in Red-Black Tree
enum Color { RED, BLACK };
// Class template for Red-Black Tree
template <typename T> class RedBlackTree {
private:
// Structure for a node in Red-Black Tree
struct Node {
T data;
Color color;
Node* parent;
Node* left;
Node* right;
// Constructor to initialize node with data and
// color
Node(T value)
: data(value)
, color(RED)
, parent(nullptr)
, left(nullptr)
, right(nullptr)
{
}
};
Node* root; // Root of the Red-Black Tree
// Utility function: Left Rotation
void rotateLeft(Node*& node)
{
Node* child = node->right;
node->right = child->left;
if (node->right != nullptr)
node->right->parent = node;
child->parent = node->parent;
if (node->parent == nullptr)
root = child;
else if (node == node->parent->left)
node->parent->left = child;
else
node->parent->right = child;
child->left = node;
node->parent = child;
}
// Utility function: Right Rotation
void rotateRight(Node*& node)
{
Node* child = node->left;
node->left = child->right;
if (node->left != nullptr)
node->left->parent = node;
child->parent = node->parent;
if (node->parent == nullptr)
root = child;
else if (node == node->parent->left)
node->parent->left = child;
else
node->parent->right = child;
child->right = node;
node->parent = child;
}
// Utility function: Fixing Insertion Violation
void fixInsert(Node*& node)
{
Node* parent = nullptr;
Node* grandparent = nullptr;
while (node != root && node->color == RED
&& node->parent->color == RED) {
parent = node->parent;
grandparent = parent->parent;
if (parent == grandparent->left) {
Node* uncle = grandparent->right;
if (uncle != nullptr
&& uncle->color == RED) {
grandparent->color = RED;
parent->color = BLACK;
uncle->color = BLACK;
node = grandparent;
}
else {
if (node == parent->right) {
rotateLeft(parent);
node = parent;
parent = node->parent;
}
rotateRight(grandparent);
swap(parent->color, grandparent->color);
node = parent;
}
}
else {
Node* uncle = grandparent->left;
if (uncle != nullptr
&& uncle->color == RED) {
grandparent->color = RED;
parent->color = BLACK;
uncle->color = BLACK;
node = grandparent;
}
else {
if (node == parent->left) {
rotateRight(parent);
node = parent;
parent = node->parent;
}
rotateLeft(grandparent);
swap(parent->color, grandparent->color);
node = parent;
}
}
}
root->color = BLACK;
}
// Utility function: Fixing Deletion Violation
void fixDelete(Node*& node)
{
while (node != root && node->color == BLACK) {
if (node == node->parent->left) {
Node* sibling = node->parent->right;
if (sibling->color == RED) {
sibling->color = BLACK;
node->parent->color = RED;
rotateLeft(node->parent);
sibling = node->parent->right;
}
if ((sibling->left == nullptr
|| sibling->left->color == BLACK)
&& (sibling->right == nullptr
|| sibling->right->color
== BLACK)) {
sibling->color = RED;
node = node->parent;
}
else {
if (sibling->right == nullptr
|| sibling->right->color == BLACK) {
if (sibling->left != nullptr)
sibling->left->color = BLACK;
sibling->color = RED;
rotateRight(sibling);
sibling = node->parent->right;
}
sibling->color = node->parent->color;
node->parent->color = BLACK;
if (sibling->right != nullptr)
sibling->right->color = BLACK;
rotateLeft(node->parent);
node = root;
}
}
else {
Node* sibling = node->parent->left;
if (sibling->color == RED) {
sibling->color = BLACK;
node->parent->color = RED;
rotateRight(node->parent);
sibling = node->parent->left;
}
if ((sibling->left == nullptr
|| sibling->left->color == BLACK)
&& (sibling->right == nullptr
|| sibling->right->color
== BLACK)) {
sibling->color = RED;
node = node->parent;
}
else {
if (sibling->left == nullptr
|| sibling->left->color == BLACK) {
if (sibling->right != nullptr)
sibling->right->color = BLACK;
sibling->color = RED;
rotateLeft(sibling);
sibling = node->parent->left;
}
sibling->color = node->parent->color;
node->parent->color = BLACK;
if (sibling->left != nullptr)
sibling->left->color = BLACK;
rotateRight(node->parent);
node = root;
}
}
}
node->color = BLACK;
}
// Utility function: Find Node with Minimum Value
Node* minValueNode(Node*& node)
{
Node* current = node;
while (current->left != nullptr)
current = current->left;
return current;
}
// Utility function: Transplant nodes in Red-Black Tree
void transplant(Node*& root, Node*& u, Node*& v)
{
if (u->parent == nullptr)
root = v;
else if (u == u->parent->left)
u->parent->left = v;
else
u->parent->right = v;
if (v != nullptr)
v->parent = u->parent;
}
// Utility function: Helper to print Red-Black Tree
void printHelper(Node* root, string indent, bool last)
{
if (root != nullptr) {
cout << indent;
if (last) {
cout << "R----";
indent += " ";
}
else {
cout << "L----";
indent += "| ";
}
string sColor
= (root->color == RED) ? "RED" : "BLACK";
cout << root->data << "(" << sColor << ")"
<< endl;
printHelper(root->left, indent, false);
printHelper(root->right, indent, true);
}
}
// Utility function: Delete all nodes in the Red-Black
// Tree
void deleteTree(Node* node)
{
if (node != nullptr) {
deleteTree(node->left);
deleteTree(node->right);
delete node;
}
}
public:
// Constructor: Initialize Red-Black Tree
RedBlackTree()
: root(nullptr)
{
}
// Destructor: Delete Red-Black Tree
~RedBlackTree() { deleteTree(root); }
// Public function: Insert a value into Red-Black Tree
void insert(T key)
{
Node* node = new Node(key);
Node* parent = nullptr;
Node* current = root;
while (current != nullptr) {
parent = current;
if (node->data < current->data)
current = current->left;
else
current = current->right;
}
node->parent = parent;
if (parent == nullptr)
root = node;
else if (node->data < parent->data)
parent->left = node;
else
parent->right = node;
fixInsert(node);
}
// Public function: Remove a value from Red-Black Tree
void remove(T key)
{
Node* node = root;
Node* z = nullptr;
Node* x = nullptr;
Node* y = nullptr;
while (node != nullptr) {
if (node->data == key) {
z = node;
}
if (node->data <= key) {
node = node->right;
}
else {
node = node->left;
}
}
if (z == nullptr) {
cout << "Key not found in the tree" << endl;
return;
}
y = z;
Color yOriginalColor = y->color;
if (z->left == nullptr) {
x = z->right;
transplant(root, z, z->right);
}
else if (z->right == nullptr) {
x = z->left;
transplant(root, z, z->left);
}
else {
y = minValueNode(z->right);
yOriginalColor = y->color;
x = y->right;
if (y->parent == z) {
if (x != nullptr)
x->parent = y;
}
else {
transplant(root, y, y->right);
y->right = z->right;
y->right->parent = y;
}
transplant(root, z, y);
y->left = z->left;
y->left->parent = y;
y->color = z->color;
}
delete z;
if (yOriginalColor == BLACK) {
fixDelete(x);
}
}
// Public function: Print the Red-Black Tree
void printTree()
{
if (root == nullptr)
cout << "Tree is empty." << endl;
else {
cout << "Red-Black Tree:" << endl;
printHelper(root, "", true);
}
}
};
// Driver program to test Red-Black Tree
int main()
{
RedBlackTree<int> rbtree;
// Inserting values into Red-Black Tree
rbtree.insert(7);
rbtree.insert(3);
rbtree.insert(18);
rbtree.insert(10);
rbtree.insert(22);
rbtree.insert(8);
rbtree.insert(11);
rbtree.insert(26);
rbtree.insert(2);
rbtree.insert(6);
// Printing Red-Black Tree
rbtree.printTree();
// Deleting nodes from Red-Black Tree
cout << "After deleting 18:" << endl;
rbtree.remove(18);
rbtree.printTree();
cout << "After deleting 11:" << endl;
rbtree.remove(11);
rbtree.printTree();
cout << "After deleting 3:" << endl;
rbtree.remove(3);
rbtree.printTree();
return 0;
}
Output
Red-Black Tree:
R----7(BLACK)
L----3(BLACK)
| L----2(RED)
| R----6(RED)
R----18(RED)
L----10(BLACK)
| L----8(RED)
| R----11(RED)
R----22(BLACK)
R----26(RED)
After deleting 18:
Red-Black Tree:
R----7(BLACK)
L----3(BLACK)
| L----2(RED)
| R----6(RED)
R----22(RED)
L----10(BLACK)
| L----8(RED)
| R----11(RED)
R----26(BLACK)
After deleting 11:
Red-Black Tree:
R----7(BLACK)
L----3(BLACK)
| L----2(RED)
| R----6(RED)
R----22(RED)
L----10(BLACK)
| L----8(RED)
R----26(BLACK)
After deleting 3:
Red-Black Tree:
R----7(BLACK)
L----6(BLACK)
| L----2(RED)
R----22(RED)
L----10(BLACK)
| L----8(RED)
R----26(BLACK)
Applications of Red Black Tree
Following are some of the common applications of red black tree:
- Red-black trees are used to implement associative arrays and set data structures in many programming languages, such as Java's TreeMap and TreeSet, and C++'s std::map and std::set.
- They are used in memory management within operating systems, particularly in the Linux kernel for efficiently organizing and searching ranges of free memory.
- Red-black trees are employed in process scheduling algorithms in operating systems, where tasks need to be efficiently inserted, removed, and searched based on priority.
- They are utilized in database management systems for implementing efficient indexing structures, allowing for fast insertion, deletion, and lookup operations.
- Red-black trees are used to implement symbol tables in compilers and interpreters, enabling efficient storage and retrieval of variable and function names.
Similar Reads
Represent Tree using graphics in C/C++
Prerequisite: graphics.h, How to include graphics.h? In C/C++ there is graphics.h header file which is used to create the object like line, circle, etc. Given an array arr[] of N integers, the task is to write C++ program to create the Tree using graphics.h. Approach: To run the program we have the
3 min read
C++ Program to Print Christmas Tree Using Pyramid
Christmas is a season of joy and festivities, and what better way to celebrate it as a programmer than by creating a Christmas tree pattern using C++? In this article, we will learn how to print a Christmas tree pattern of desired height using simple nested loops in C++.Approach to Print Christmas T
4 min read
Inorder Tree Traversal of Binary Tree in C++
A binary tree is a non-linear hierarchical data structure in which each node has at most two children known as the left child and the right child. As the binary tree has non-linear structure it can be traversed in multiple ways one such way is in-order traversal which is a depth first (DFS) traversa
4 min read
Zig-Zag traversal of a Binary Tree using Recursion
Given a binary tree, the task is to find the zigzag level order traversal of the tree. In zig zag traversal starting from the first level go from left to right for odd-numbered levels and right to left for even-numbered levels. Approach:The zigzag traversal of a binary tree involves traversing the t
11 min read
Flatten Binary Tree in order of Zig Zag traversal
Given a Binary Tree, the task is to flatten it in order of ZigZag traversal of the tree. In the flattened binary tree, the left node of all the nodes must be NULL.Examples: Input: 1 / \ 5 2 / \ / \ 6 4 9 3 Output: 1 2 5 6 4 9 3 Input: 1 \ 2 \ 3 \ 4 \ 5 Output: 1 2 3 4 5 Approach: We will solve this
7 min read
Implementation of AVL Tree using graphics in C++
AVL Trees are self-balancing Binary Search Trees where the difference between heights of left and right subtrees cannot be more than one for all nodes. Below is the example of the AVL Tree: In this article, we will be implementing the concept of AVL Tree using graphics in C++. As a prerequisite, one
9 min read
Binary Search Tree in C++
A Binary Search Tree (BST) is a type of binary tree in which the data is organized and stored in a sorted order. Unlike, a binary tree that doesn't follow a specific order for node placement, in a binary search tree all the elements on the left side of a node are smaller than the node itself, and el
10 min read
Convert Binary Tree to Circular Doubly Linked List using Linear extra space
Given a Binary Tree, convert it to a Circular Doubly Linked List. The left and right pointers in nodes are to be used as previous and next pointers, respectively, in the converted Circular Linked List.The order of nodes in the List must be the same as in the order of the given Binary Tree.The first
12 min read
Tree C/C++ Programs
Trees are hierarchical data structures that contain nodes connected by edges. They are recursive in nature, which means that they are made up of smaller instances of themselves. Various types such as binary tree, trie, etc. have different characteristics to make them suitable for different applicati
2 min read
Deletion of a given node K in a Binary Tree using Level Order Traversal
Given a binary tree and a node K, the task is to delete the node K from it by making sure that tree shrinks from the bottom (i.e. the deleted node is replaced by bottom-most and rightmost node) using Level Order Traversal. Examples: Input: K = 8, Tree = Output: Explanation: Please Refer below for ex
13 min read