I. Red/black tree Overview
Similar to the AVL tree we have learned before, the red and black trees maintain a balance between the binary search trees through specific operations during insertion and deletion operations to achieve high search performance. However, since the emergence of the red and black trees, the AVL tree has been put in the museum. It is said that the red and black trees have better efficiency and higher statistical performance. This is a deep understanding of the implementation principles of the red and black trees.
The difference between a red-black tree and an AVL Tree is that it uses color to identify the height of a node. It pursues a local balance rather than a very strict balance in the AVL Tree. Those who have learned the data structure should have learned the complexity of the AVL tree, but the complexity of the AVL Tree is nothing more than that of the red and black trees. The red and black trees are truly abnormal data structures.
Because the underlying implementations of the Associative containers in STL are all red and black trees by default, the red and black trees are very important for the follow-up study of STL source code. It is necessary to master the implementation principle and source code implementation of the red and black trees.
The red-black tree is a variant of the AVL Tree. The red-black tree uses some coloring rules to ensure that no path is twice longer than other paths, so as to achieve a close balance. The so-called red/black tree is not only a binary search tree, but also must meet the following rules:
1. Each node is either red or black.
2. The root node is black.
3. If the node is red, its child nodes must be black.
4. Any path from any node to null (tail end of the tree) must contain the same number of black nodes.
These constraints guarantee a balance in the tree, which also determines that the insert, delete, query, and other operations of the red/black tree are relatively fast. According to rule 4, the new node must be in red. According to rule 3, the new node father node must be in black. When a newly added node reaches its insertion point according to the Binary Search Tree rule, but fails to meet the preceding conditions, it must adjust the color and rotate the tree, for example:
Suppose we insert nodes 3, 8, 35, and 75 respectively. According to the Binary Search Tree rules, after these four nodes are inserted, we will find that they all break the rules of the red and black trees, therefore, we must adjust the tree, that is, rotate the tree and change the color of the node.
2. Insert nodes on the red/black tree
Before discussing the insert operation of the red/black tree, you must understand that the initial color of any new node to be inserted is red. This is easy to understand, because inserting black spots will increase the number of black nodes on a path, resulting in an imbalance in the black height of the entire tree. However, if the parent node of the new node is red (as shown in), it will violate the nature of the Red-black tree: two adjacent red nodes cannot appear on a path. In this case, a series of operations are required to maintain a balance between the red and black trees.
To clearly indicate the insert operation, the following words "new" are used in the node to indicate a newly inserted node. The "parent" word is used to represent the parent node of the newly inserted node; use "uncle" to represent the sibling node of the "parent" node, and use "ancestor" to represent the parent node of the "parent" node. Insert operations are divided into the following situations:
1. Black parent
As shown in, if the parent node of the new node is a black node, inserting a red dot will not affect the balance of the red and black trees. At this time, the insert operation is completed. One of the advantages of the Red-black tree over the AVL Tree is that the black tree is more common, so that the probability that the red-black tree needs to rotate is less than that of the AVL Tree.
2. Red parent
If the parent node of the new node is red, a series of operations are required to ensure that the entire tree is red and black. As shown in, because the parent node is red, it can be determined that the grandfather node must be black. In this case, you need to determine the operation based on the color of the uncle node. The blue node indicates that the color is unknown. Since it may be necessary to perform multiple rotation operations on the path from the root node to the new point, the starting point of each imbalance judgment (we can regard it as the new point) is different. So here we use a blue arrow to point to this starting point, which is called a judgment point.
2.1 reduncle
When the uncle node is red, as shown in, you do not need to rotate it. You only need to change the father and uncle nodes to black and change the grandfather node to red. However, because the parent node of the grandfather node may be red, it violates the red-black tree nature. In this case, the grandfather node must be used as the new judgment point to continue up (Iteration.
Note that no matter whether the "parent node" is on the left or right of the "Uncle node", whether the "new node" is the Left or Right child of the "parent node, their operations are the same (in fact, this situation includes four types, you only need to adjust the color, do not need to rotate the tree ).
2.2 uncle Hei
When the uncle node is black, it needs to be rotated to show the possibility of all rotation:
Case 1:
Case 2:
Case 3:
Case 4:
It can be observed that after the rotation is complete, all the new rotating roots are black. At this time, you do not need to backtrack up to perform the balancing operation. The insertion operation is complete. Note that the "uncle", "1", "2", and "3" nodes in the above four figures may be the blackguard nodes.
In fact, the insert operation of the red and black trees is not very difficult, or even easier than the insert operation of the AVL Tree. The source code of the insert operation is as follows:
// Insert insert_unique () // Insert a new value: the node key value cannot be repeated. If it is repeated, the insert is invalid. // note that the returned value is a pair, the first element is a red-black tree iterator pointing to the newly added node // The second element indicates whether the insert is successful or not. <class key, class value, class keyofvalue, class compare, class alloc> pair <typename rb_tree <key, value, keyofvalue, compare, alloc >:: iterator, bool> rb_tree <key, value, keyofvalue, compare, alloc> :: insert_unique (const value & V) {rb_tree_node * Y = header; // root node root parent node rb_tree_no De * x = root (); // bool comp = true from the root node; while (X! = 0) {Y = x; comp = key_compare (keyofvalue () (v), key (x); // is the key value of the current node smaller than the key value of the current node? X = comp? Left (x): Right (x); // when it is large, it is left. When it is less than or equal to, it is right.} // after leaving the while loop, Y refers to the parent node of the inserted vertex (it must be a leaf node at this time) iterator J = iterator (y); // Let iterator J point to the parent node of the inserted vertex yif (COMP) // If comp is true when the while loop is left (if it is "large", it will be inserted to the left) {If (j = begin ()) // if the parent node of the insertion point is the leftmost node return pair <iterator, bool> (_ insert (x, y, z), true ); else // otherwise (the parent node of the insertion point is not the leftmost node) -- J; // adjust J and prepare for testing.} If (key_compare (Key (J. node), keyofvalue () (v) // the new key value is not the same as the key value of the existing node. Therefore, the following insert operation returns pair <iterator, Bool> (_ insert (x, y, z), true); // above, X is the new value insertion point, and Y is the parent node of the insertion point, V is the new value. // This indicates that the new value must be the same as the key value in the tree. Therefore, the new value return pair <iterator, bool> (J, false) should not be inserted );} // truly Insert the execution program _ insert () template <class key, class value, class keyofvalue, class compare, class alloc> typename <key, value, keyofvalue, compare, alloc >:: _ insert (base_ptr X _, base_ptr Y _, const value & V) {// The parameter X _ is the new value insertion point, and the parameter Y _ is the parent node of the insertion point, the parameter V is the new value link_type x = (link _ Type) X _; link_type y = (link_type) Y _; link_type Z; // key_compare is the key value size comparison criterion. It should be a function objectif (y = header | X! = 0 | key_compare (keyofvalue () (v), key (y) {z = create_node (V); // generate a new node left (y) = z; // when Y is the header, leftmost () = zif (y = header) {root () = z; rightmost () = z ;} else if (y = leftmost () // if Y is the leftmost node leftmost () = z; // maintain leftmost (), make it always point to the leftmost node} else {z = create_node (V); // generate a new node right (y) = z; // make the new node the parent node of the insertion point. If (y = rightmost () = z; // maintain rightmost (), make it always point to the rightmost node} parent (z) = y; // set Left (z) = 0 for the parent node of the new node; // set right (z) = 0 for the left subnode of the new node; // set the right child node of the new node // the color of the new node will be set in _ rb_tree_rebalance () (and adjusted) _ rb_tree_rebalance (z, header-> parent ); // parameter 1 is a new node, and parameter 2 is the root node root ++ node_count; // return iterator (z) when the number of nodes is accumulated; // return an iterator, point to new node} // global function // reschedule the tree (change color and rotate the tree) // parameter 1 is the Add node, parameter 2: Root Node rootinline void _ rb_tree_rebalance (_ rb_tree_node_base * X, _ rb_tree_node_base * & root) {X-> color = _ rb_tree_red; // The new node must be red while (X! = Root & X-> parent-> color = _ rb_tree_red) // The parent node is red {If (X-> parent = x-> parent-> left) // The parent node is the left child node of the grandfather node {_ rb_tree_node_base * Y = x-> parent-> right; // make y the primary node if (Y & Y-> color = _ rb_tree_red) // The primary node exists, red {X-> parent-> color = _ rb_tree_black; // change the parent node to black y-> color = _ rb_tree_black; // change the uncle node to black X-> parent-> color = _ rb_tree_red; // change the grandfather node to red X = x-> parent ;} else // No uncle node, or the uncle node is black Color {If (x = x-> parent-> right) // if the new node is the right child node of the parent node {x = x-> parent; _ rb_tree_rotate_left (X, root); // The first parameter is left-side.} X-> parent-> color = _ rb_tree_black; // change the color x-> parent-> color = _ rb_tree_red; _ rb_tree_rotate_right (X-> parent, root ); // The first parameter is the right-hand vertex} else // The parent node is the right child node of the grandfather node {_ rb_tree_node_base * Y = x-> parent-> left; // set Y to an uncle node if (Y & Y-> color = _ rb_tree_red) // has an uncle node, it is red {X-> parent-> color = _ Rb _ Tree_black; // change the parent node to black y-> color = _ rb_tree_black; // change the primary node to black X-> parent-> color = _ rb_tree_red; // change the grandfather node to red X = x-> parent; // prepare to continue the layer-up check} else // No uncle node, or the uncle node is black {If (x = x-> parent-> left) // if the new node is the left child node of the parent node {x = x-> parent; _ rb_tree_rotate_right (x, root); // The first parameter is the right-hand vertex} X-> parent-> color = _ rb_tree_black; // change the color x-> parent-> color = _ rb_tree_red; _ rb_tree_rotate_left (X-> parent, Root); // The first parameter is left-side }}// whileroot-> color = _ rb_tree_black; // The root node is always black} // left-hand function inline void _ rb_tree_rotate_left (_ rb_tree_node_base * X, _ rb_tree_node_base * & root) {// X indicates the rotation point _ rb_tree_node_base * Y = x-> right; // set Y to the right subnode of the rotation point X-> right = Y-> left; if (Y-> left! = 0) Y-> left-> parent = x; // do not forget to configure the parent node y-> parent = x-> parent; // make y replace x completely (the relationship between x and its parent node must be completely received) if (x = root) // X is the root node root = y; else if (x = x-> parent-> left) // X is the left subnode of its parent node X-> parent-> left = y; else // X is the right child node of its parent node X-> parent-> right = y; y-> left = x; X-> parent = y ;} // right-hand function inline void _ rb_tree_rotate_right (_ rb_tree_node_base * X, _ rb_tree_node_base * & root) {// X indicates the rotation point _ rb_tree_node_base * Y = x-> left; // set Y to the left of the rotation point Child node X-> left = Y-> right; if (Y-> right! = 0) Y-> right-> parent = x; // do not forget to configure the parent node y-> parent = x-> parent; // make y replace x completely (the relationship between x and its parent node must be completely received) if (x = root) root = y; else if (x = x-> parent-> right) // X is the right sub-node of its parent node X-> parent-> right = y; else // X is the left subnode of its parent node X-> parent-> left = y; y-> right = x; X-> parent = y ;}