There are a lot of red-black tree jokes on the Internet, and many people say that red-black trees only exist in jokes, not in interviews or real projects. Take a look at what people are saying:

Usually, if an interviewer asks me the number red and black. I usually turn around and walk away. Not because it’s not the job to ask. It’s because. I TMD really no – – | | |

A lot of people looked at this netizen said, feel very piercing. Don’t worry, here’s something more poignant:

It’s so hard! Map map = new TreeMap(); Manual squint, that’s done

But yes, TreeMap is built with a red-black tree. Learning red-black tree is not just for the interviewer, martial arts novels say: moves are just a form, to practice magic, must understand the mind. This article will take you slowly away from the red – black tree veil, especially in the article dynamic graph will let you feel the rotation of the red – black tree is very intuitive. Of course, if you understand this article, your job interview will be easy

As we know, binary search tree is a good data structure, can quickly find a given keyword of the data items, and can quickly insert and delete data items. The problem with binary search trees, however, is that if random data is inserted into the tree, it performs very well, but if ordered or reversed data is inserted, the binary search tree performs very slowly. Because when numeric order is inserted, the binary tree is not balanced, and when placed on a line, it becomes a linked list… Its ability to quickly find, insert, and delete specified data items is lost.

To search a tree in fast O(logN) time, the tree needs to always be balanced (or at least mostly balanced), which means that for each node in the tree, the number of descendants to its left should be roughly equal to the number of descendants to its right. A red-black tree is such a balanced tree. For an item to be inserted, the insertion routine checks whether it will break the characteristics of the tree. If it does, the program will correct it, changing the structure of the tree as needed to keep the tree balanced. So what are the characteristics of red-black trees?

1. Characteristics of red-black trees

It has two main characteristics: 1. The nodes have color; 2. Follow rules to keep these colors in different permutations during inserts and deletions. The first feature is easily solved by adding a data field, such as a Boolean variable, to the node class to represent the color information of the node. The second feature is more complicated. Red-black trees have several rules, and if they follow these rules, then the tree is balanced. The main rules for red-black trees are as follows:

  • 1. Each node is either red or black;

  • 2. The root node is always black;

  • 3. If the node is red, its children must be black (and vice versa);

  • 4. Each path from root node to leaf node or empty child node must contain the same number of black nodes (that is, the same black height).

It is no accident that nodes inserted into red-black trees are all red, because inserting a red node is less likely to violate the red-black rule than inserting a black node. The reason: inserting a black node always changes the black height (against Rule 4), but inserting a red node only half the time violates Rule 3. Also, a violation of Rule 3 is easier to fix than a violation of Rule 4. This balance can be broken when a new node is inserted, so how is the red-black tree corrected?

2. Balance correction

Red-black trees modify their balance in three main ways, changing node color, left and right rotation. This seems a little abstract, so let’s go through them separately.

2.1 change color

Changing the node color is easier to understand because it violates rule 3. Suppose you now have A node E, and then you insert node A and node S, and node A is on the left child, and node S is on the right child. If another node is inserted at this point, then there is an imbalance, because the children of the red node must be black, but the newly inserted node is red. So you have to change the node color. So we change the two children of the root from red to black (more on why both are changed when we insert them below) and the parent node from black to red. It can be shown in the following schematic diagram:

2.2 a left-handed

Usually the left-handed operation is used to rotate a red link that leans to the right to the left. The schematic diagram is as follows:

Left-handed rotation has a lovely dynamic diagram that you can easily understand:

3 right-lateral

Right-handed rotation is just the opposite to left-handed rotation. I won’t repeat it here, just look at the schematic diagram:

Of course, dextral rotation also has a cute motion picture:

There are three main ways to correct the balance of red-black trees. We have a perceptual understanding, so when to correct? Which fixes should be used when? That’s what we’re going to talk about.

3. Red-black tree operation

The basic operations of red-black trees are add, remove, and rotate. What kind of rotation is used to correct the balance of red-black trees that may be disturbed by additions or deletions? We first introduce the nodes of red-black tree, and then analyze the specific implementation of left-handed and right-handed respectively. Finally, we discuss the specific operation of red-black tree.

3.1 Nodes of red-black tree

The red-black tree is an improvement on the binary search tree, so the nodes are similar to the binary search tree, except that a Boolean variable is added to represent the node color.


     
  1. public class RBNode<T extends Comparable<T>>{

  2. boolean color; / / color

  3. T key; // Keyword (key)

  4. RBNode<T> left; // Left child node

  5. RBNode<T> right; // Right child node

  6. RBNode<T> parent; / / the parent node

  7.    public RBNode(T key, boolean color, RBNode<T> parent, RBNode<T> left, RBNode<T> right) {

  8.        this.key = key;

  9.        this.color = color;

  10.        this.parent = parent;

  11.        this.left = left;

  12.        this.right = right;

  13.    }

  14.    public T getKey() {

  15.        return key;

  16.    }

  17.    public String toString() {

  18.        return "" + key + (this.color == RED? "R" : "B");

  19.    }

  20. }

Copy the code

3.2 Concrete realization of left-handed rotation

The concept of left-handedness is already perceptual, so I will not repeat it here. Let’s explore the concrete implementation of left-handedness from the following code combined with the diagram above:


     
  1. /************* Left-rotate the red-black tree node x ******************/

  2. / *

  3. * Left-turn schematic: Left-turn the node X

  4. *     p                       p

  5. *    /                       /

  6. *   x                       y

  7. *  / \                     / \

  8. * lx  y      ----->       x  ry

  9. *    / \                 / \

  10. *   ly ry               lx ly

  11. * Left-handed rotation does three things:

  12. * 1. Assign the left child of y to the right child of x, and assign x to the parent of the left child of y (when the left child of y is not null)

  13. * 2. Assign the parent of x to the parent of y, and update the child of P to y(left or right)

  14. * 3. Set the left child node of y to x and the parent node of x to y

  15. * /

  16. private void leftRotate(RBNode<T> x) {

  17. //1. Assign the left child of y to the right child of x and x to the parent of the left child of y (when the left child of y is not null)

  18.    RBNode<T> y = x.right;

  19.    x.right = y.left;

  20. if(y.left ! = null)

  21.        y.left.parent = x;

  22. //2. Assign the parent of x to the parent of y, and update the child of P to y(left or right)

  23.    y.parent = x.parent;

  24.    if(x.parent == null) {

  25. this.root = y; // If the parent of x is empty, set y to the parent

  26.    } else {

  27. If (x == x.parent.left) // if x is the left child

  28. x.parent.left = y; // set y to the left child as well

  29.        else

  30. x.parent.right = y; // Otherwise set y to the right child

  31.    }

  32. //3. Set the left child of y to x and the parent of x to y

  33.    y.left = x;

  34.    x.parent = y;      

  35. }

Copy the code

3.3 Concrete realization of dextral rotation

The concept of dextral rotation has been perceptual understanding, and there is no need to repeat here, let’s explore the specific implementation of dextral rotation from the following code combined with the diagram above:


     
  1. /************* Perform right-rotation on the red-black tree node y ******************/

  2. / *

  3. * Schematic diagram of left rotation: Perform right rotation on node Y

  4. *        p                   p

  5. *       /                   /

  6. *      y                   x

  7. *     / \                 / \

  8. *    x  ry   ----->      lx  y

  9. *   / \                     / \

  10. * lx  rx                   rx ry

  11. * Dextral does three things:

  12. * 1. Assign the right child of x to the left child of y and y to the parent of the right child of X (when the right child of x is not null)

  13. * 2. Assign the parent of y to the parent of x, and update the child of P to x(left or right)

  14. * 3. Set the right child node of x to y and the parent node of y to x

  15. * /

  16. private void rightRotate(RBNode<T> y) {

  17. //1. Assign the left child of y to the right child of x and x to the parent of the left child of y (when the left child of y is not null)

  18.    RBNode<T> x = y.left;

  19.    y.left = x.right;

  20. if(x.right ! = null)

  21.        x.right.parent = y;

  22. //2. Assign the parent of x to the parent of y, and update the child of P to y(left or right)

  23.    x.parent = y.parent;

  24.    if(y.parent == null) {

  25. this.root = x; // If the parent of x is empty, set y to the parent

  26.    } else {

  27. If (y == y.parent.right) // If x is the left child

  28. y.parent.right = x; // set y to the left child as well

  29.        else

  30. y.parent.left = x; // Otherwise set y to the right child

  31.    }

  32. //3. Set the left child of y to x and the parent of x to y

  33.    x.right = y;

  34.    y.parent = x;      

  35. }

Copy the code

3.4 Insertion Operations

After analyzing the major rotation operations in red-black trees, let’s move on to the common insert, delete, and so on. Let’s start with the insert operation. Since red-black tree is an improvement of binary search tree, the first half of insert operation works the same, that is, find the position to be inserted first, and then insert the node. Let’s look at the first half of insert code:


     
  1. / * * * * * * * * * * * * * * * * * * * * * * * to the red-black tree insert node * * * * * * * * * * * * * * * * * * * * * * /

  2. public void insert(T key) {

  3.    RBNode<T> node = new RBNode<T>(key, RED, null, null, null);

  4. if(node ! = null)

  5.        insert(node);

  6. }

  7. // Insert a node into a red-black tree, the same process as a binary search tree

  8. private void insert(RBNode<T> node) {

  9. RBNode<T> current = null; // Represents the parent of the last node

  10. RBNode<T> x = this.root; // It is used to search down

  11. //1. Find the insertion position

  12. while(x ! = null) {

  13.        current = x;

  14.        int cmp = node.key.compareTo(x.key);

  15.        if(cmp < 0)

  16.            x = x.left;

  17.        else

  18.            x = x.right;

  19.    }

  20. node.parent = current; // Find the location of the current node as the parent node

  21. //2. Then determine whether node is inserted into the left or right child node

  22. if(current ! = null) {

  23.        int cmp = node.key.compareTo(current.key);

  24.        if(cmp < 0)

  25.            current.left = node;

  26.        else

  27.            current.right = node;

  28.    } else {

  29.        this.root = node;

  30.    }

  31. //3. Re-shape it into a red-black tree

  32.    insertFixUp(node);

  33. }

Copy the code

This is exactly the same idea as the binary search tree, and I won’t go over it here, but let’s look at the last step in the method, insertFixUp. Because the tree may be unbalanced after insertion, the insertFixUp method is mainly discussed by case, analyzing when to change color, when to turn left, when to turn right. Let’s start with a theoretical analysis of the specific case, and then look at the implementation of the insertFixUp method.

If it is the first time, the original tree is empty, so it only violates rule 2 of red-black trees, so just black out the root node. If the parent of the inserted node is black, it does not violate the rules of red-black trees; nothing needs to be done; But there are three situations where we start to change color and rotate:

  • 1. The parent node of the inserted node and its uncle node (another child node of the grandfather node) are both red;

  • 2. The parent node of the insert node is red, the uncle node is black, and the insert node is the right child of its parent node.

  • 3. The parent node is red, the uncle node is black, and the parent node is the left child node of the parent node.

Let’s take a look at each of the three cases and then give the implementation code.

For case 1: the parent of the inserted node and its uncle node (another child of the grandfather node) are both red. In this case, there must be a grandparent, but we don’t know if the parent is the left child or the right child, but because of the symmetry, we just have to figure out what happens on one side, and the other case will also happen on the other. Consider the case where the parent node is the left child of the grandfather node, as shown in the left figure below:

In this case, we will do the following: black the parent node (5) and uncle node (8) of the current node (4), and red the grandfather node (7), as shown in the upper right figure. Then point the current node to its grandfather node and start the algorithm again from the new current node (see the program below for details). So this becomes case 2.

For case 2, the parent of the inserted node is red, the uncle node is black, and the inserted node is the right child of its parent. We need to do the following operations: take the parent node (2) of the current node (7) as the new node, and use the new current node as the fulcrum to perform left-rotation operation. This is done as shown in the lower left, so the lower left becomes case 3.

In case 3: the parent node of the inserted node is red, the uncle node is black, and the inserted node is the left child node of the parent node. We need to do the following operations: black the parent node (7) of the current node, red the grandfather node (11), right rotation on the grandfather node as the fulcrum. Finally, the root node is blackened, and the entire red-black tree is restored to balance, as shown in the upper right. At this point, the insert is complete!

We can see that if is from 1 began to happen, will walk the case 2 and 3, that is to say, this is the whole process, of course, in practice may not occur from case 1, if the starting condition 2, can adjust it to walk again a case 3, the if directly by adjusting case 3, the first two are not need to be adjusted. Therefore, the sequence relationship between discoloration and rotation can be expressed as: discoloration -> left-rotation -> right-rotation.

At this point, we are done inserting. Let’s take a look at the implementation of the insertFixUp method.


     
  1. private void insertFixUp(RBNode<T> node) {  

  2. RBNode<T> parent, gparent; // Define parent and grandparent nodes

  3. // The parent node exists and its color is red

  4. while(((parent = parentOf(node)) ! = null) && isRed(parent)) {

  5. gparent = parentOf(parent); // Get the grandparent node

  6. // If the parent node is the left child of the grandfather node, else does the opposite

  7.        if(parent == gparent.left) {                  

  8. RBNode<T> uncle = gparent.right; // Get the uncle node

  9. //case1: Uncle node is also red

  10. if(uncle ! = null && isRed(uncle)) {

  11. setBlack(parent); // Black out the parent and uncle nodes

  12.                setBlack(uncle);  

  13. setRed(gparent); // Color the grandfather node red

  14. node = gparent; // Place the location at the grandfather node

  15. continue; // continue while, rejudge

  16.            }  

  17. //case2: The uncle node is black and the current node is the right child node

  18.            if(node == parent.right) {  

  19. leftRotate(parent); // Rotate left from the parent node

  20. RBNode<T> tmp = parent; // Then swap the parent node with yourself in preparation for the following right-handed rotation

  21.                parent = node;  

  22.                node = tmp;  

  23.            }  

  24. //case3: The uncle node is black and the current node is the left child node

  25.            setBlack(parent);  

  26.            setRed(gparent);  

  27.            rightRotate(gparent);  

  28. } else {// If the parent node is the right child of the grandfather node, it is the opposite of the above, and the essence is the same

  29.            RBNode<T> uncle = gparent.left;  

  30. //case1: Uncle node is also red

  31. if(uncle ! = null & isRed(uncle)) {

  32.                setBlack(parent);  

  33.                setBlack(uncle);  

  34.                setRed(gparent);  

  35.                node = gparent;  

  36.                continue;  

  37.            }  

  38. //case2: The uncle node is black and the current node is the left child node

  39.            if(node == parent.left) {  

  40.                rightRotate(parent);  

  41.                RBNode<T> tmp = parent;  

  42.                parent = node;  

  43.                node = tmp;  

  44.            }  

  45. //case3: The uncle node is black and the current node is the right child node

  46.            setBlack(parent);  

  47.            setRed(gparent);  

  48.            leftRotate(gparent);  

  49.        }  

  50.    }  

  51. // Set the root node to black

  52.    setBlack(this.root);  

  53. }  

Copy the code

Big cock-a-doody monkey rich generation

I have calculated that the profit per click of the advertisement below is 0.7 yuan

↓↓ ↓↓ ↓ Click below to support