Red-black tree is a very good performance data structure, the key is that it can ensure that the worst performance is also logarithmic, mainly because it is a balanced tree, so also called the balance of the search tree. To understand the red and black trees, it's best to take a look at my previous blog, "Algorithm 4" symbol table and the two-fork lookup tree, to understand the binary lookup tree and why we need to balance the search tree. 2-3 Find Tree
The tree height in the binary lookup tree is affected by the input data, in extreme cases a tree and a linked list are no different, so we need a tree, all of its leaf nodes to the root node of the distance is equal, the tree is a balanced tree, and with the data, the balance will remain. The following is a theoretical balance tree--2-3 lookup tree.
2-3 tree Diagram
The main feature of the 2-3 tree is that the tree consists of an ordinary 2 node and a three node. The attributes in the 2-node and two-fork lookup trees are the same. For 3 nodes, it has two keys and three links, and all elements in the left subtree to the left link are less than two keys in the 3 node, the sub-tree size of the middle link is between two keys, and the element in the subtree to which the right link points is greater than two keys. Find
For the lookup operation and the two-fork lookup tree Basically the same, the recursive comparison to find the key and the root node of the tree, less than the left to continue to find, greater than the right to find, equal to find hit. The difference is that for 3 nodes, there must be an intermediate node, in the case of the size of the three node two keys, to the intermediate subtree recursion. Finally, if you find an empty connection to the leaf node, it returns null directly. Insert
The insertion of 2-3 trees is relatively complex because it is the key to ensuring the balance of the tree itself. We will discuss it in several cases. inserting to a 2 node
Insert first to find, to find the immediate update, if the missing to insert a new element, insert a new element must be on the leaf node of the empty connection, if the leaf node is a 2 node, then directly inserted, let the special synthesis of a 3 node. Obviously, the height of the tree did not change. The schematic is as follows
This is the case of inserting to a 2 node. inserting to a 3 node
If you insert into a 3 node, the first thing we can do is to put them together as a 2 node to form a 4 node (with three elements), and then decompose the 4 node, inserting the intermediate elements into their parent element, leaving two elements into two 2 nodes. Note: only the middle element can be taken up, because this can guarantee the order of the tree, that is, the left and right elements relative to the root element of the size of the relationship , and then the parent node, such as the parent node is originally a 2 node, then the direct insert into a 3 node, the insert operation is completed. If the original parent node is a 3 node, then you can still repeat the process, continue to add the intermediate elements to the parent node, if the process continues to the root node, then we split the formation of a temporary 4 node root node, to get three 2 nodes, while the entire tree height increased by 1.
figure above is a schematic of the root node splitting
This insertion is equivalent to inserting elements into this can cause the height of the tree to change the disadvantage of storing in 3 nodes, with the insertion of 3 nodes to the root node, and then through the root of the division of the entire tree is increased by 1, it can be seen that 3 nodes and the associated insertion method is to ensure the balance of the key, It can also be seen that the growth of 2-3 trees grows from the bottom up through the root node. 2-3 trees can be achieved in the worst-case condition also has logarithmic performance.
Below we can see a 2-3 tree concrete implementation-red and black Binary search tree, hereinafter referred to as the Red black tree. red and black trees
The previous article has mentioned that the 3 node is the key to achieve balance, here we use a red link is a red left link to represent the 3 node
The 2 node is represented by a normal black connection.
Then a red black tree should be perfect black balance, that is, from any empty connection to the root node experience the number of black connections should be the same. Add one more condition: no node is connected with two red links at the same time , then the red and black tree can correspond with 2-3 trees. Node Code
Private static Final Boolean red = true;//defines red as true
private static final Boolean BLACK = false;
private Node root;
Private class node{
key key;
Value value;
int N;
Node left, right;
The Boolean color;//represents
the color Node (key key, Value value,int N,boolean color) {
This.key = Key;
This.value = value;
This. n = n;
This.color = color;
}
}
Private Boolean isred (Node x) {
if (x==null) return false;
return X.color = = RED;
}
Here we add a Boolean variable that represents the color. One of the keys here is that the color of a node refers to the color of the connection that points to the node. Rotate
Rotation is a very important operation. We do not change the order of the tree, a red link from the left link to the right link, or from the right link to the left link, which in some cases, such as corresponding to the 2-3 tree to the 3 node insertion element, the update of the whole tree is very useful.
The code is shown in the figure, so don't repeat it again.
There's also a flipcolors () operation
It can be seen that this function corresponds to the 2-3 tree to insert the intermediate element into the parent node, because it turns the original two red link into a black link, the equivalent of splitting into two 2 nodes, and the middle element because the color is red, so it joins the parent node. Flipcolors () has other functions in the back, so here I give it the final form.
private void Flipcolors (Node h) {
h.color =!h.color;
H.left.color =!h.left.color;
H.right.color =!h.right.color;
}
In fact, the color of the reverse, this form is clearly inclusive of the form of the image.
Note: The root node is All Black Find
The search algorithm for the red and black trees is exactly the same as the search algorithm for the two-fork search tree, that is, for the search algorithm, the node in the red-black tree or the color of the link is not used, but there is no relationship, although the red black tree is only black link balance, but even if not consider the color of the search, The whole tree will not appear the most extreme situation in the binary tree, so the performance is still guaranteed. Insert
Red black tree insertion algorithm is more complex, for 2-3 trees relatively simple, but in the concrete implementation, each 3 node is a concrete structure, then we have to adjust these specific structure after inserting, in order to achieve the function of 2-3 trees. inserting to a 2 node
Inserting a 2-node into a 2-3 tree is straightforward and merges directly into a 3-node line. However, when the implementation, because the relative to the parent node may be very small, then at the time of insertion may be on the left or right side of the parent node, and the red link can only be left link, then when the right insert, it is necessary to rotate the right link into the left link. inserting to a 3 node
Inserting to a 3 node is more complicated because there is not only a problem with the insertion direction, but also the parent node is red, we have to adjust the structure of several nodes to implement the operation of inserting the middle node into the parent node in the 2-3 tree. There are three kinds of cases. 1
If the two child nodes of the next node are red, then we can easily implement the 2-3 tree by Flipcolors () to insert the middle node into the parent node, the nodes are independent into two 2 nodes, while maintaining the order (the default middle node is black, Because the entire tree is ordered by default before inserting, this can be ensured by proper insertion. 2
In the second case, the first red link needs to be rotated to the right, so it becomes the first case, which can be handled according to Case 1 3
In the third case, the following red link must be left rotated to become the second case, then the second case can be processed
The above figure corresponds to three kinds of cases from left to right respectively. By handling the above three cases, we can match the insertion of the red and black tree with the 2-3-tree insertion algorithm one by one. The following is the inserted code.
public void put (Key key,value value) {
root = put (root,key, value);
Find the key value, you can not find a new
Root.color = BLACK;
}
Private node put (node H,key Key, value value) {
if (h==null) return new Node (Key, value, 1, RED);
int cmp = Key.compareto (h.key);
If (cmp<0) H.left = put (H.left, key, value);
Recursively finds
the else if (cmp>0) h.right = put (h.right, key, value);
else h.value = value;
if (isred (h.right) &&!isred (h.left)) h=rotateleft (h);
if (isred (h.left) && isred (h.left.left)) h=rotateright (h);
if (isred (h.left) && isred (h.right)) flipcolors (h);
H.N = Size (h.left) +size (h.right) +1;
Update the n value of the traversed node
return h;
One of the notable things in this is that the three-line if condition sentence, because it is placed after the recursive statement, so it is equivalent to go down the tree down the end or find the equivalent, processing and then return to run, you can see that the three sentences can be completed, but also easy to test, This statement is fully compatible with the first two cases. So the process of continually returning to the root node runs these three sentences, which is equivalent to moving the possible redundant nodes to the 2-3 tree. The process of the node. The last tree is balanced. Delete minimum and maximum values and delete
Remove the trouble, we first consider the deletion of the minimum value, when we delete a 3 node element of the time is OK, directly deleted after leaving a 2 node, the tree's balance has not changed. But deleting a 2 node directly can cause a change in the height of the tree. So, we still have to deal with, from the top down to the transformation, the ultimate goal is to ensure that at the time of deletion of the current node is not just a 2 node. Delete Minimum value
The minimum is on the leftmost side, we need to merge three 2 nodes to form a 4 node along the left side, or three nodes on the right, to "borrow" one from the right node to form a 3 node or 4 node, so that the current node is greater than 2 nodes
private node Moveredleft (node h) {//This function is used to handle 2 nodes of the flipcolors (h);//The above node is "pulled down" to form a large node
if (isred (H.right.left)) {//H.right = Rotateright (h.right);
h = rotateleft (h); Flipcolors (h);//Note ... The code in this chapter of the algorithm 4 book lacks this line, which represents borrowing a node and then another to the parent node.
Otherwise we will become a big node together with the brothers node.
} return h;
} public void Delmin () {if (!isred (root.left) &&!isred (root.right)) Root.color = RED;
Root = Delmin (root);
if (!isempty ()) Root.color = BLACK;
} private node Delmin (node h) {if (H.left = = null) return null;
if (!isred (h.left) &&!isred (H.left.left))//means that the left child node of H is a 2 node h= moveredleft (h);
h.left= delmin (H.left);
Return balance (h); }
where the balance () function is to undo the temporary 4 node when it returns, so that the entire tree is balanced again. The code below, is in the above put () function inside the three line if conditional sentence before adding a sentence, in fact, for why add this sentence I do not understand, began to think because the minimum value after the deletion of h.left is null, but isred (NULL) returned is false, Then feel this sentence and the following sentence function is repetitive, and in the experiment, the deletion of this sentence example can be correctly exported.
Private node balance (node h) {
if (isred (h.right)) H = rotateleft (h);
if (isred (h.right) &&!isred (h.left)) h=rotateleft (h);
if (isred (h.left) && isred (h.left.left)) h=rotateright (h);
if (isred (h.left) && isred (h.right)) flipcolors (h);
H.N = Size (h.left) +size (h.right) +1;
return h;
}
Delete Maximum value
Deleting the maximum is similar to deleting the minimum, but because the red link is a left link, the code is different.
Private Node Moveredright (node h) {//function is similar to Moveredright (), but the direction is
flipcolors (h) to the right;
if (isred (H.left.left)) {
h=rotateright (h);
Flipcolors (h);
}
return h;
}
Public void Delmax () {
if (!isred (root.right) &&!isred (root.left))
root.color = RED;
Root = Delmax (root);
if (!isempty ()) Root.color = BLACK;
}
Private node Delmax (node h) {
if (isred (h.left))
h=rotateright (h);//Guaranteed Direction Consistency
if (h.right = = null)
Return null;//Find the right-most node and delete it.
if (!isred (h.right) &&!isred (h.right.left))
h= moveredright (h);//The Right child node is 2 nodes, run this function to turn it into at least 3 nodes
h.right = Delmax (h.right);
Return balance (h);
}
The Delmin () function is used for the actual delete function
public void Delete (key key) {if (!isred (root.right) &&!isred (root.left)) Root.color = RED;
root = delete (Root,key);
if (!isempty ()) Root.color = BLACK; } Private node Delete (node H, key key) {if (Key.compareto (H.key) <0) {if (!isred (h.left) &&A
MP;!isred (h.left.left)) h= moveredleft (h);
H.left = delete (H.left, key);
} else {if (isred (h.left)) h= rotateright (h);
if (Key.compareto (H.key) ==0 && (h.right==null)) return null;
if (!isred (h.right) &&!isred (h.right.left)) h= moveredright (h);
if (Key.compareto (H.key) ==0) {H.value = Get (h.right, Min (h.right). Key);
H.key = min (h.right). Key;
H.right = Delmin (h.right);
} Else H.right = delete (H.right, key); } return Balance (h);
}
Let's examine the deleted code. First of all, in the public Delete () The transformation of the root node of the behavior of the color, I do not know, does not seem to be necessary, the book is just simple in the answer to this part of the said, but also not very clear, I tried to do not change the color example seems to be able to run.
In the following private delete () function, the first is to find, the small root node in the record continue to find, in the middle, encountered 2 nodes to use the Moveredleft () function to construct a temporary 3 or 4 nodes, convenient to finally delete. is greater than or equal to the root node, but it can be seen that in this piece of code, the first three sentences are basically delmax (), this is because along the right subtree down the process, we still have to ensure that the current node is not 2 nodes, and this is required by Delmax (). The last conditional sentence is very good understanding, that is, we found the target key, but this key is not a leaf node, then we will be the right subtree of the key and the smallest node of the key and value are assigned to it, and then delete the right subtree of the minimum value, so that the target key is deleted and the whole tree is orderly, balanced. Analysis
The red and black trees are almost completely balanced, but we do not use the nature of color when we look for them, so what does the so-called red and black do? I think the color is the role of dynamic adjustment , the most extreme situation in the binary tree is to insert elements in an orderly manner, and finally get a tall tree equals the number of inserted elements n, but this will not happen in the red and black trees, the reader can experiment, even if the orderly way to insert, That worst-case scenario will not happen, and a red-black tree of size n will not exceed 2lgN 2lgN, with an average height of 1.00lgN ~1.00lgn. This is because if a path is all 3 nodes, then the maximum path is 2lgN 2lgN. The reason I think is that the new node is inserted in red, and any one node can not be linked to two red links.