Graphic red black tree

Basic structure of red black tree

  red black tree is a self balancing binary lookup tree. It is a data structure used in computer science. It is commonly used in associative arrays, dictionaries, etc. The internal data structure of Standard Association containers set, multiset, map and multimap in C + + is red black tree.

Definition of red black tree:

  1. Each node can only be red or black
  2. The root node is black
  3. Each leaf node is black
  4. If a node is red, its child nodes must be black
  5. The number of black nodes passing from any node to leaf node is the same

2-3 tree

Before introducing the red black tree, first understand its equivalent form 2-3 tree, which is very helpful to understand the definition of red black tree.
2-3 definition of tree:

  1. Properties of satisfying binary search tree

  2. A node can store one (2-node) or two (3-node) keywords

  3. Each node has two or three child nodes

Those who have studied B-trees will find that their nodes are similar to B-trees, similar to third-order B-trees. The basic operation of 2-3 tree is similar to that of B-tree.
2-3 basic operations of tree:

  1. Insert: when inserting a new node, insert it into the leaf node
  2. Decomposition: the 4-node can be decomposed into a tree composed of three 2-nodes, and the root node of the decomposed tree should be merged upward with its original parent node.

Next, press sequence
1 , 2 , 3 , 4 , 5 {1,2,3,4,5} 1,2,3,4,5
Build a 2-3 tree:

It is not difficult to find that in the above operations, the 2-3 tree can always maintain a strict balance. However, because the number of node keywords is not unique and the programming implementation of split merge operation is complex, we hope to convert it into a binary tree by adding some rules, and the converted binary tree still has the self balancing advantage of 2-3 tree, which is red black tree.

2-3 tree - > red black tree

There are different conversion rules for the two nodes of the 2-3 tree:

  • 2-node: Black node directly converted to red black tree
  • 3-node: disassemble two keywords. The left keyword is marked in red (indicating that the red node and its parent node were siblings in the 2-3 tree), the right keyword is marked in black, and the right keyword is the parent node of the left keyword

According to the above rules, we will
3 , 5 , 8 , 10 , 12 , 15 , 16 , 18 , 19 , 4 , 20 {3,5,8,10,12,15,16,18,19,4,20} 3,5,8,10,12,15,16,18,19,4,20
The constructed 2-3 tree is transformed into a red black tree:

Review the properties of red black tree and judge whether the tree in the figure above meets all properties:

  • Each node can only be red or black
  • The root node is black
  • Each leaf node is black
  • If a node is red, its child nodes must be black
  • The number of black nodes passing from any node to leaf node is the same

It can be found that all properties are satisfied except the third property. In fact, the leaf node in property 3 refers to an empty leaf node. Therefore, the complete red black tree should be:

In this way, all properties are satisfied.

Creation of red black tree

  as mentioned earlier, the code for creating a 2-3 tree is complex, so we certainly won't create a 2-3 tree first and then convert it into a red black tree. Because we can easily create a binary tree, a red black tree has more properties than an ordinary binary tree. Therefore, when creating a red black tree, we only need to add several operations on the basis of the method of creating a binary tree to ensure that the properties of the red black tree are not destroyed.

Insert: as mentioned above, the insertion operations of 2-3 trees all occur at the leaf node, and the elements to be inserted are fused with the leaf node first. Therefore, the newly inserted node and the leaf node in the original position are in the same level relationship. As mentioned earlier, the red node and its parent node used to be the original level relationship, so the inserted node should be red.
Creation demonstration: the child node connected by the red edge in the figure below is what we call the red node (the fourth edition of the algorithm says that the node connected by the red branch is a red node. By analogy, it doesn't make any difference, because a branch only corresponds to one node).

Observing the above creation process, it is found that when the insertion node is located in the right branch, we need left rotation, because the red node can only appear in the left branch.

Pseudo code implementation of left-handed operation:

RBNode* leftRotate(RBNode* node)
{
    RBNode *r_temp = node -> right;
    node -> right = r_temp -> left;
    r_temp -> left = node;
    r_temp -> color = node -> color;
    node -> color = RED;
    return r_temp;
}

The right-hand operation is similar to the left-hand operation:

RBNode* rightRotate(RBNode* node)
{
    RBNode *l_temp = node -> left;
    node -> left = l_temp -> right;
    l_temp -> right = node;
    l_temp -> color = node -> color;
    node -> color = RED;
    return l_temp;
}

Flip color operation:

void flipColor(RBNode *node)
{
    node -> color = RED;
    node -> left -> color = BLACK;
    node -> right -> color = BLACK;
}

Insert operation:

With the implementation basis of the above basic operations, let's study the implementation of insertion operation. Insertion is divided into three cases according to the insertion position:

Implementation of insert operation:

RBNode* insert(RBNode *&root, KeyType key, ValueType value)
{
	if(root == NULL)
		return new RBNode(key, value, 1, RED);// The inserted node is red

	if(key < root->key) 	 	 root -> left  = insert(root->left, key, value);
	else if(key > root->key) 	 root -> right = insert(root->right, key, value);
        else                 	root -> value = value;

	// Actions to deal with three situations
	if(isRed(root->left) && !isRed(root->left))		root = leftRotate(root);	
	if(isRed(root->left) && isRed(root->left->left))	root = rightRotate(root);
	if(isRed(root->left) && isRed(root->right))		flipColor(root);

	root -> N = size(root->left) + size(root->right) + 1;
	return root;
}

Overall code

#include <bits/stdc++.h>

#define RED true
#define BLACK false
using namespace std;
#define KeyType int
#define ValueType string
class RBNode {

	friend bool isRed(RBNode *node);
	friend RBNode* leftRotate(RBNode *node);
	friend RBNode* rightRotate(RBNode *node);
	friend void flipColor(RBNode *node);
	friend RBNode* insert(RBNode *&root, KeyType key, ValueType value);
	friend int size(RBNode *node);
	friend RBNode* search(RBNode *root, KeyType key);

public:
	RBNode(KeyType k, ValueType v, int n, bool c) : key(k), value(v), N(n), color(c), left(NULL), right(NULL) { }

private:
	KeyType key;			// Key value saved by node
	ValueType value;		// Value associated with key value
	RBNode *left, *right;	// Left and right child node pointers
	int N;					// Number of nodes in this subtree
	bool color;				// Node color: true is red, false is black
};

// Get the number of nodes of the tree with node as the root
int size(RBNode *node)
{
	if(node == NULL) return 0;
	return node -> N;
}

// Get node color
bool isRed(RBNode *node)
{
	if(node == NULL) return false;
	return node->color == RED;
}

// Change node color
void flipColor(RBNode *node)
{
	node -> color = RED;
	node -> left -> color = BLACK;
	node -> right -> color = BLACK;
}

// Left hand operation
RBNode* leftRotate(RBNode *node)
{
	RBNode *temp  = node -> right;
	node -> right = temp -> left;
	temp -> left  = node;
	temp -> color = node -> color;
	node -> color = RED;
	temp -> N     = node -> N;
	node -> N     = 1 + size(node->left) + size(node->right);
	return temp;	// Returns or resets the parent node pointer
}

// Right hand operation
RBNode* rightRotate(RBNode *node)
{
	RBNode *temp  = node -> left;
	node -> left  = temp -> right;
	temp -> right = node;
	temp -> color = node -> color;
	node -> color = RED;
	temp -> N     = node -> N;
	node -> N     = 1 + size(node->left) + size(node->right);
	return temp;	// Returns or resets the parent node pointer
}

RBNode* search(RBNode* root, KeyType key)
{
	if(root == NULL) return NULL;
	else
	{
		if(root -> key == key) return root;
		else if(root -> key < key)
		{
			return search(root -> left, key);
		}
		else
		{
			return search(root -> right, key);
		}
	}
}


RBNode* insert(RBNode *&root, KeyType key, ValueType value)
{
	if(root == NULL)
		return new RBNode(key, value, 1, RED);

	if(key < root->key) 	 	 	root -> left  = insert(root->left, key, value);
	else if(key > root->key) 	root -> right = insert(root->right, key, value);
	else 											root -> value = value;

	// Actions to deal with three situations
	if(isRed(root->left) && !isRed(root->left))				root = leftRotate(root);	
	if(isRed(root->left) && isRed(root->left->left))	root = rightRotate(root);
	if(isRed(root->left) && isRed(root->right))							 flipColor(root);

	root -> N = size(root->left) + size(root->right) + 1;
	return root;
}

reference resources

Algorithm 4th Edition - red black tree - Robert Sedgewick
"I drew 20 pictures to explain the red and black tree to my girlfriend" -Programmer Xiao Wu

Tags: Algorithm data structure

Posted on Wed, 22 Sep 2021 13:48:23 -0400 by Wayne Herbert