## preface

In this article, I will introduce the most famous self balanced binary search tree - red black tree.

The mangrove tree was invented by Rudolph bell, a professor of information technology at Munich University of science and technology. In 1978, in a paper published by Robert Sedgwick, this data structure was officially named red black tree, and a complete proof of complexity was provided.

Although the red black tree is cumbersome, it has very high efficiency in practice. Because the red black tree is self balanced (the red black tree actually realizes a pseudo balance, which will be mentioned later), it will not degenerate into a chain table in extreme cases. You can find elements in the time of $O(logN) $, Insert and delete operations. Because of its excellent performance, it is widely used in the design of some underlying data structures - for example, TreeMap and TreeSet in Java language are red black trees.

Next, let's explore what kind of data structure the red black tree is.

## Red black tree and 2-3 tree

Before introducing the famous red black tree, let's take a look at the data structure of 2-3 tree. Understanding 2-3 tree in advance is very helpful for us to learn red black tree next, because the principles of red black tree and 2-3 tree are interlinked, and 2-3 tree is easier to understand than complex red black tree.

What is a 2-3 tree? Firstly, 2-3 tree is an absolutely balanced multitree.

We all know the concept of balanced binary tree - balanced binary tree is a binary search tree. It is either an empty tree, or the absolute value of the height difference between its left subtree and its right subtree (the height of the left subtree - the height of the right subtree, which is also called the balance factor) does not exceed 1, and the left and right subtrees of a balanced binary tree are a balanced binary tree.

As shown in the following figure, the tree is a balanced binary tree:

The so-called absolute balance is more strict than the balance in the balanced binary tree. The definition of absolute balance is that for any node in the tree, the height of the left and right subtrees is the same.

As shown in the above figure, the schematic diagram shows a 2-3 tree. This tree satisfies the definition of absolute balance.

In addition, 2-3 tree is not a standard binary tree, but a multi fork tree. In the 2-3 tree above, we can see that some nodes store one element and some nodes store two elements. The node that stores one element is called a 2-node, while the node that stores two elements is called a 3-node. There are only these two types of nodes in the 2-3 tree, which is also the origin of the name of the 2-3 tree.

Next, I will demonstrate the operation of inserting elements into the 2-3 tree. Let's take a look at the magical operation of the 2-3 tree to maintain the absolute balance of the whole tree.

First, we insert a node "42" into an empty 2-3 tree, and then insert a node "37":

The principle of inserting nodes into 2-3 tree is the same as that of binary search tree, but it also follows another principle: new nodes will never be inserted into an empty position.

If the tree is a binary search tree, node "37" will be inserted into the left child of node "42", but in this way, the absolute balance of the 2-3 tree will be destroyed. Therefore, node "37" will not be inserted into an empty position, but merged with node "42" to form a new 3-node:

Next, we insert the element "12" into the 2-3 tree:

According to our principle, "a new node will never be inserted into an empty position", and node "12" will merge with the current 3-node into a "4-node":

Then, the 4-node will be split into three 2-nodes to maintain the absolute balance and ensure to follow the definition of 2-3 tree:

We continue to insert the element "18" into the current 2-3 tree:

Insert element "6" into the current 2-3 tree:

Insert element "11" into the current 2-3 tree:

Insert element "5" into the current 2-3 tree:

Through the demonstration of the above simulation diagrams, you must have been able to preliminarily master the characteristics of 2-3 tree and how to maintain the absolute balance of 2-3 tree when adding elements to 2-3 tree.

As we said before, the essence of a red black tree is the same as that of a 2-3 tree. If you understand a 2-3 tree, it will not be difficult to understand the red black tree I want to introduce next. There are two kinds of nodes in a 2-3 tree: a 2-node and a 3-node. A 2-node stores one element and a 3-node stores two elements. A red black tree is a "2-3 tree in the form of a binary tree" , it uses a single black node to represent a 2-node and "red black" to represent a three node:

In this way, all red nodes in the red black tree must be left child nodes, so we also call this red black tree "left leaning red black tree".

For example, a 2-3 tree:

This is what it looks like after it is converted to a red black tree:

This is why we say that a red black tree is equivalent to a 2-3 tree. Any 2-3 tree can be converted into a red black tree in this way.

## Implementation of red black tree

### Basic properties of red black tree

Red black tree has the following five basic properties:

1. Each node is either red or black.

There is nothing to say about this. After all, it is called a red black tree. Nodes are either red or black.

2. The color of the root node must be black.

We can consider the 2-3 tree by analogy. The nodes of the 2-3 tree are either 2-nodes or 3-nodes. No matter which node is used as the root node of the red black tree, the color of the root node must be black.

3. Each leaf node (the last empty node) must be black.

If the red black tree is empty, the root node is also empty. This and the second point can correspond to each other, because the color of the root node must be black.

4. If a node is red, its child nodes are black.

This can also be compared to the next 2-3 tree. The left and right nodes of the red node in the red black tree are either 2-nodes or 3-nodes. No matter what kind of node, the black node must be connected to the red node.

5. From any node to leaf node, the number of black nodes passing through is the same.

After the above 2-3 tree is converted into a red black tree, it is as follows:

From this picture, we can clearly see that from any node to leaf node, the number of black nodes is the same.

This is what we call - a red black tree is a binary tree that maintains "black balance" (black nodes are absolutely balanced). Strictly speaking, a red black tree is not a balanced binary tree. However, because the red black tree has the characteristics of black balance, it can be considered that the red black tree maintains an approximate balance.

From here, we can also analyze the time complexity of adding, searching and deleting elements to the red black tree. In the worst case, on the path from the root node to the leaf node, the red node and the black node appear alternately, and its complexity is $O(2logN) $. Ignoring the constant term coefficient, we can get that the time complexity of adding, deleting and modifying the red black tree is $O(logN) $.

### Code implementation of adding elements to red black tree

Next, let's look at how to add elements to the red black tree.

It is not difficult to define and implement nodes in red black tree. Because the red black tree itself is a binary search tree, the node can store element values, and there are internal pointers to left and right children. These are the same as the implementation of nodes in the binary search tree. However, the nodes of the red black tree have different colors, and the color is either red or black. We use an additional boolean variable to represent the color of the node:

public class Node<E> { public E e; public Node left; public Node right; public boolean color; public Node(E e) { this.e = e; left = null; right = null; color = true; // true indicates red and false indicates black } }

As you can see, we set the color of initializing a node to red. In fact, it is not difficult to understand. When introducing the 2-3 tree, we mentioned that the principle of adding nodes to the 2-3 tree is that new nodes will never be inserted into an empty position. When adding nodes, we must first fuse the nodes to be added with other nodes. If the color of the initialization node is red, it means that the newly added node is fused with other nodes.

Before clarifying the logic of adding a node to the red black tree, let's first review the logic of inserting a node into the binary search tree:

If the root node of the current binary search tree is empty, the newly inserted node will become the root node.

If the root node of the current binary search tree is not empty, let the root be the node of the current comparison: the newly inserted node is compared with the current node; If the value is smaller than the current node, go left. If the value is larger than the current node, go right. Then let the node of the next layer continue to be the node of the current comparison until it reaches the position where it should be inserted.

The following figure shows the process of adding node "28" to the current binary search tree:

Java code:

/** * @param e Add a new element to the binary search tree */ public void add(E e) { root = add(e, root); } /** * @param e Newly inserted node into binary search tree * @param node Node currently compared * @return Returns the root node of the binary search tree */ private Node add(E e, Node node) { if (node == null) { size++; return new Node(e); } if (e.compareTo((E) node.e) < 0) { node.left = add(e, node.left); } else if (e.compareTo((E) node.e) > 0) { node.right = add(e, node.right); } return node; }

We need to ensure that the root node of the red black tree is always black, so every time we insert a node, we manually set the root node to black:

public void add(E e) { root = add(e, root); root.color = BLACK; // false }

Based on the logic of inserting a node into the binary search tree, we insert a new node into the red black tree in the following cases:

- The currently inserted node is fused with the 2-node
- The currently inserted node is fused with the 3-node

If we insert into a 2-node, there are two cases:

In the first case, the inserted node is smaller than the element value of the current 2-node.

If this is the case, we can directly integrate the new node and 2-node into a 3-node:

In the second case, the inserted node is larger than the element value of the current 2-node.

Insert the element "42" into the position of the right child of "37":

At this time, the definition of left leaning red black tree is not satisfied. We need to perform a "left rotation" operation with node "37" (left rotation is counterclockwise and right rotation is clockwise), and reset the color:

In order not to lose generality, I have added corresponding subtrees to the child nodes of node "37" and node "42" respectively. Through the logic of pseudo code in the figure above, we can see that the tree after "left rotation" maintains the characteristics of both red black tree and binary search tree.

The Java code for the left rotation operation is as follows:

/** * Left rotation: * * node x * / \ / \ * T1 x =========> node T3 * / \ / \ * T2 T3 T1 T2 * */ private Node leftRotate(Node node){ Node x = node.right; // Left rotation node.right = x.left; x.left = node; x.color = node.color; node.color = RED; return x; }

After discussing all the cases of fusion between the newly inserted node and the 2-node, let's take a look at the fusion between the newly inserted node and the 3-node.

If we insert into a 3-node, there are several situations:

In the first case, the value of the new node is larger than that of the black node on the 3-node:

After inserting node "66":

This operation is equivalent to the fusion of our newly inserted node and 3-node into a 4-node, corresponding to the 2-3 tree. Our operation is as follows:

Next, we need to turn the current 4-node into three 2-nodes and let "42" continue to merge upward:

Therefore, correspondingly, in the red black tree, we should change nodes "37" and "66" into black 2-nodes, and set node "42" as the red node that continues to fuse with the previous node:

The operation of this step is called flipColors. As the name suggests, it is to flip all the colors of the three nodes. The corresponding code is as follows:

// flipColors private void flipColors(Node node){ node.color = RED; node.left.color = BLACK; node.right.color = BLACK; }

In the second case, the value of the new node is smaller than that of the red node on the 3-node:

After node "12" is inserted:

At this point, we need to right rotate node "42":

Do flipColors again:

The Java code for right rotation is as follows:

/** * Right rotation: * * node x * / \ / \ * x T2 ===========> y node * / \ / \ * y T1 T1 T2 * */ private Node rightRotate(Node node) { Node x = node.left; // Right rotation node.left = x.right; x.right = node; x.color = node.color; node.color = RED; return x; }

In the third case, the value of the new node is larger than that of the red node on the 3-node, but smaller than that of the black node:

After node "40" is inserted:

At this time, we first rotate the "37" node to the left, then rotate the "42" node to the right, and finally perform the flipColors operation:

Generally speaking, the operation of adding elements to the red black tree has the following logic:

The complete Java code for adding nodes to the red black tree is as follows:

public class RBTree<E extends Comparable<E>> { private static final boolean RED = true; private static final boolean BLACK = false; private Node root; private int size; public RBTree() { root = null; size = 0; } public int size() { return size; } public boolean isEmpty() { return size == 0; } /** * Judge the color of node * * @param node * @return */ public boolean isRed(Node node) { if (node == null) return false; return node.color; } /** * @return Returns the root node of the red black tree */ public Node getRoot() { return root; } /** * Left rotation: * * node x * / \ / \ * T1 x ===========> node T3 * / \ / \ * T2 T3 T1 T2 * */ private Node leftRotate(Node node){ Node x = node.right; // Left rotation node.right = x.left; x.left = node; x.color = node.color; node.color = RED; return x; } /** * Right rotation: * * node x * / \ / \ * x T2 ===========> y node * / \ / \ * y T1 T1 T2 * */ private Node rightRotate(Node node) { Node x = node.left; // Right rotation node.left = x.right; x.right = node; x.color = node.color; node.color = RED; return x; } // Color flip private void flipColors(Node node){ node.color = RED; node.left.color = BLACK; node.right.color = BLACK; } /** * @param e Add a new element to the red black tree */ public void add(E e) { root = add(e, root); root.color = BLACK; } /** * @param e Newly inserted node into red black tree * @param node Node currently compared * @return Returns the root node of the red black tree */ private Node add(E e, Node node) { if (node == null) { size++; return new Node(e); // Red nodes are inserted by default } if (e.compareTo((E) node.e) < 0) { node.left = add(e, node.left); } else if (e.compareTo((E) node.e) > 0) { node.right = add(e, node.right); } if(isRed(node.right) && !isRed(node.left)) node = leftRotate(node); if(isRed(node.left) && isRed(node.left.left)) rightRotate(node); if(isRed(node.left) && isRed(node.right)) flipColors(node); return node; } }

So far, the operation of inserting a node into the red black tree has been introduced.

The above content is my summary of the red black tree. The operation of deleting nodes from the red black tree will not be covered in this article.

## summary

Today, I mainly shared the data structure of red black tree.

Before introducing the red black tree, we first introduce the 2-3 tree. If we understand the 2-3 tree, it is not difficult to understand the red black tree.

Then we introduce the five basic characteristics of red black tree:

- Each node is either red or black
- The color of the root node must be black
- Each leaf node (the last empty node) must be black
- If a node is red, its child nodes are black
- From any node to leaf node, the number of black nodes passing through is the same

The five basic characteristics of red black tree are analyzed from the principle of 2-3 tree.

Finally, we introduce how to add a node to the red black tree and its code implementation.

Well, so far, this article has been here. Welcome to my official account. Here, I hope you can gain more knowledge, and we will see you next time!