The basic operations of the Binary Search Tree, including searching, inserting, deleting, and obtaining the maximum and minimum values, can all be implemented in the O (h) time complexity. Therefore, the O (lgn) time can be used in the desired time) but the balance of the binary search tree is not maintained in these operations, so its height may become very high. When its height is high, the performance of the binary search tree may not be better than that of the linked list. Therefore, the set operation of the binary search tree is the expected time O (lgn), and the worst case is O (n ).
The red-black tree is also a binary search tree, which has the properties of the Binary Search Tree, while the red-black tree also has some other special properties, this makes the basic O (lgn) Operation of the dynamic set of the red and black trees in the worst case. The red and black trees add colors and other restrictions to the nodes, in this way, the path from a node in the red/black tree to a leaf node is no longer than twice that of other paths. Therefore, the tree balance can be maintained. The nodes in the red/black tree have five fields: color, keyword, left node, right node, and parent node, and direct no child node or parent node pointer to an Nil sentinel node, this nil node is called a leaf node and an external node. Other nodes containing keywords are called internal nodes. The reason for adding such an empty outer node is to facilitate some border operations on the red and black trees, especially when deleting the node, this nil node is very useful.
Five properties of the red/black tree:
1. Each node is either red or black.
2. The root node is black.
3. The leaf node (nil node) is black.
4. If a node is red, its child nodes are black.
5. For any node, all paths from the node to its child node contain the same number of black nodes.
On any path from a node to a leaf node, the number of black nodes is the black height of the node BH (X ). The black height of the Red-black tree is defined as the black height of the root node. The red/black tree performs some searches, taking the maximum and minimum values, and taking the successor node without modifying the red/black tree. Therefore, the operations on the tree are the same as those on the binary search tree, but for the insert and delete operations, because the red/black tree has been modified, some additional operations must be performed to maintain its nature. In fact, these additional operations will not change the time complexity of the insert and delete operations, it is still O (lgn ).
To maintain the properties of the red/black tree, you need to perform some operations unique to the red/black tree: rotation, including left-hand and right-hand operations.
Left-hand: To do left-hand operation on node X, make sure that its right child y is not a nil node, and rotate it with the branch axis between x and y, let y replace X as the new root of the Child tree, and X as the left child of Y, the original left child of Y becomes the right child of X. The time complexity is O (1 ).
Right-hand: In contrast to left-hand, to perform right-hand operation on node X, you must ensure that its left-hand child y is not a nil node, and the axis is rotated from X to Y, let y replace X as the new root of the Child tree, and X as the right child of Y, the original right child of Y becomes the left child of X. The time complexity is O (1 ).
After you insert or delete a red/black tree, you need to perform additional maintenance operations. These operations are built on the rotation. The following describes the problems that may occur after a node is inserted or deleted in the red/black tree and the solutions:
1. Insert nodes
Insert a new node into the red/black tree. In order not to increase the number of black nodes (which affects the black height), we will color the newly inserted nodes in red, in this way, it may violate the nature of 2 and 4 after insertion. When the outstanding nature of 2 and 4 is not in violation of the Nature of 5, consider these three properties. Therefore, the main task of maintenance is to change the structure and color of the nodes in the red/black tree to black, and change the parent node of the inserted red node to Black (if not black ).
2. delete a node
After a node is deleted from the red/black tree, if the node is red, the red/Black attribute is not damaged. Because the deleted node is a red node, it is not the root node, attribute 2 is okay, and the black node is not changed, so attribute 5 is okay, because the parent node of the deleted node is black, therefore, no matter what color the secondary node is, it will not damage properties 4. properties 1 and 3 are not affected, so all properties are not damaged. If the deleted node is black, the black height of the node changes and the property 5 is damaged, the main task of maintenance is to increase the height of the Child tree by modifying the structure and color of the node.
After a node is inserted or deleted, there is only one situation in which the properties of the red and black trees can be maintained through transformation, in other cases, it is necessary to convert the conversion to this export situation that can be directly solved. For specific solutions, see the following code annotations.
/** Introduction to algorithms Chapter 1 red/black tree */# include <iostream> using namespace STD; // define the color Enum color {black = 0, Red = 1 }; // define the red/black tree node typedef struct rbtnode {color; int key; rbtnode * left, * right, * parent; rbtnode () {This-> color = color: red; this-> key = 0; this-> left = This-> right = This-> parent = NULL;} rbtnode (color C, int K, rbtnode * l, rbtnode * r, rbtnode * P) {This-> color = C; this-> key = K; this-> left = L; this-> parent = P; this -> Right = r ;}} rbtnode, rbtree; // defines a sentinel nilnode, to facilitate the handling of border issues // This is especially useful when deleting red/black tree elements // If the deleted node has no children, nilnode can be used as its child to facilitate the addition of rbtnode * nilnode = NULL; /** in-order traversal * recursion */void inordertreewalkrecursion (rbtree * tree) {If (Tree & tree! = Nilnode) {inordertreewalkrecursion (tree-> left); cout <tree-> key <""; inordertreewalkrecursion (tree-> right );}} /** find the smallest element in the binary sorting tree * That is, the leftmost element * time complexity is O (lgn) */rbtnode * rbtreeminimum (rbtree * tree) {If (! Tree | tree = nilnode) return NULL; while (tree-> left! = Nilnode) {tree = tree-> left;} return tree ;} /** calculate the ordinal traversal of the specified node in the binary sorting tree * If the right subtree of the node is not empty, then, the node is the smallest node in the right subtree of the node * otherwise, the right subtree of the node is empty and must be traced back to the first node: this node is the left child of its parent node * followed by its parent node. If such a node does not exist, the node is the rightmost node, and the successor is blank * time complexity is O (lgn) */rbtnode * rbtreesuccessor (rbtnode * node) {If (! Node | node = nilnode) return NULL; If (node-> right! = Nilnode) {return rbtreeminimum (node-> right);} rbtnode * temp = node-> parent; while (temp! = Nilnode & node = temp-> right) {node = temp; temp = node-> parent;} return temp ;} /** left-handed red/black tree node */void leftrotate (rbtree * & tree, rbtnode * node) {// If node is empty or its right node is empty, if (! Node | node = nilnode | node-> right = nilnode) return; // make the left child of the right node of the node become the right child of the node rbtnode * temp = node-> right; node-> right = temp-> left; If (temp-> left! = Nilnode) {temp-> left-> parent = node;} // Replace the node's right child with the node location temp-> parent = node-> parent; if (node-> parent = nilnode) {// indicates that node is the root node and the pointer tree of the root node is changed to temp ;} else {If (node = node-> parent-> left) {node-> parent-> left = temp ;} else {node-> parent-> right = temp ;}// make node the left child of the right child temp-> left = node; node-> parent = temp ;} /** right-hand red/black tree node */void rightrotate (rbtree * & tree, rbtnode * node) {// If node is empty or its left node is empty, if (! Node | node = nilnode | node-> left = nilnode) return; // make the right child of the Left node of the node become the left child of the node rbtnode * temp = node-> left; node-> left = temp-> right; if (temp-> right! = Nilnode) {temp-> right-> parent = node;} // Replace the node's right-left child with the node location temp-> parent = node-> parent; if (node-> parent = nilnode) {// indicates that node is the root node and the pointer tree of the root node is changed to temp ;} else {If (node = node-> parent-> left) {node-> parent-> left = temp ;} else {node-> parent-> right = temp;} // make node the right child of its left child temp-> right = node; node-> parent = temp ;} /** after an element is inserted into the red/black tree, adjust the tree to keep the property of the red/black tree. * The main time consumed is to increase the illegal position because the height is O (lgn ), therefore, the time complexity is still O (lgn) *, and the rotation cannot exceed 2 times. The overall time complexity is O (lgn) */void rbinsertfixup (rbtree * & tree, rbtnode * node) {If (! Tree |! Node) return; // because the color of the newly inserted node is red, it may only violate the nature 2 (the root node is black) and the Nature 4 (the child node of the red node can only be black) // if the parent node of a node is not a nilnode, the red and black trees are violated only when the parent node is red, you need to adjust // otherwise you do not need to adjust while (node-> parent-> color = color: Red) {// mainly consider node's uncle node rbtnode * unclenode = NULL; if (node-> parent = node-> parent-> left) {unclenode = node-> parent-> right; if (unclenode-> color = color: Red) {// Case 1: node's uncle node is red, move the offending location up // because the node's parent node and Uncle node are both red, the node's grandfather node must be black // Node's parent node and Uncle node are both black, and the grandfather node is red. // This keeps the overall black height unchanged, keep the property 5 (the number of black nodes from any node to its child node is the same) node-> parent-> color = color: black; unclenode-> color = color :: black; node-> parent-> color = color: red; node = node-> parent;} else {// Scenario 2: node's uncle node is black // case 2.1: node is the right child of its parent node and converts the case 2.1 to 2.2 for processing, because node and its parent node are both red, therefore, if (node = node-> parent-> right) is not affected when the parent node is rotated) {node = node-> parent; leftro Tate (tree, node); // Changes from 2.1 to 2.2} // case 2.2: node is the left child of its parent node, this is the export scenario // because the parent node is red and the uncle node is black, the grandfather node must be black, so the parent node is changed to black // The grandfather node is red, the height of the node is not changed along the right-hand side of the grandfather node, and the parent node of the node becomes black. // The main goal is to make the parent node of the node become black, to keep the property 4 // The Black height cannot be changed in this process, because the property 5node-> parent-> color = color: black; node-> parent-> color = color: red; rightrotate (tree, node-> parent );}} else {// the location of the uncle node is the opposite. The processing method is the same as above, but the orientation is the opposite when the node is rotated. unclenode = node-> parent-> left; If (unclenode-> C Olor = color: Red) {// scenario 1: If the node's uncle node is red, move the offending location up node-> parent-> color = color: black; unclenode-> color = color: black; node-> parent-> color = color: red; node = node-> parent ;} else {// Case 2: node's uncle node is black // case 2.1: node is its parent node's left child if (node = node-> parent-> left) {node = node-> parent; rightrotate (tree, node); // Changes from 2.1 to 2.2} // case 2.2: node is the right child of its parent node, in this case, node-> parent-> color = color: black; node-> parent-> P Arent-> color = color: red; leftrotate (tree, node-> parent) ;}}// preserve nature 2, set the root node as a black tree-> color = color: black;}/** insert element * to search for the insertion point at O (lgn) * maintaining the property time of the red/black tree is O (lgn) * so the total time complexity is O (lgn) */void rbinsert (rbtree * & tree, rbtnode * node) {If (! Tree |! Node) return; rbtnode * posnode = nilnode; rbtnode * t = tree; while (T! = Nilnode) {posnode = T; If (node-> key <t-> key) {T = T-> left;} else {T = T-> right ;}} node-> parent = posnode; If (posnode = nilnode) {tree = node;} else {If (node-> key <posnode-> key) {posnode-> left = node;} else {posnode-> right = node ;}} // node-> left = node-> right = nilnode; node-> color = color: red; // insert a red node to ensure that the black height remains unchanged // maintain the red/black tree rbinsertfixup (tree, node );} /** maintain the properties of the red/black tree after the nodes are deleted. * The main time consumed is to increase the illegal location, and the time complexity is O (Lgn) * rotation up to 3 times, total O (1) * so the time complexity is O (lgn) */void rbdeletefixup (rbtree * & tree, rbtnode * node) {// If the node is the root node, the black height of the entire tree is reduced by one without any impact. // If the node color is red, you can directly black it out. // otherwise, maintenance is required. After the red/black tree node is deleted, the maintenance nature is mainly related to the node's sibling node while (node! = Tree & node-> color = color: Black) {// the current node is a non-root black node. Consider its sibling node rbtnode * brothernode = NULL; if (node = node-> parent-> left) {brothernode = node-> parent-> right; // Case 1: The sibling node is red, convert it to a sibling node that is black. 2 // The sibling node is black and the sibling node is red, the parent node must be black // and the child node of the sibling node must be black. Therefore, the parent node can be red and the sibling node can be black // and then left-handed along the parent node, the black height is not affected, at the same time, the new brother node of node becomes the left black child of the original brother node // Therefore, the brother node of node becomes the black node if (brothernode-> color = color: Red) {node-> parent-> color = color: red; brothernode-> Color = color: black; leftrotate (tree, node-> parent); brothernode = node-> parent-> right;} // Scenario 2: black if (brothernode-> right-> color = color: Black & brothernode-> left-> color = color: black) {// case 2.1: The right child of a brother node is black and the left child is black. // The node is black at this time, the sibling node and the two child nodes are also black. // at this time, the height of the node subtree is 1 lower than that of the Child tree of its sibling node. Therefore, the sibling node can be red, in this way, the black height is the same. // This reduces the entire Black height of the node's parent node sub-tree by 1 (violation of rule 5) and changes the node's parent node to a new node, raise the illegal location // continue the cycle brothernode-> color = color: red; node = node-> Parent;} else {If (brothernode-> right-> color = color: Black) {// case 2.2: The right child of the sibling node is black, and the left child is red, convert this situation to 2.3 // because the brother node is black, the left child is red, and the right child is black, therefore, you can blacklist the left child of the sibling node. // The left child of the sibling node becomes red, and then right-handed along the sibling node to enter the situation 2.3, and change the black height of brothernode-> left-> color = color: black; brothernode-> color = color: red; rightrotate (tree, brothernode ); brothernode = node-> parent-> right;} // case 2.3: The right child of the sibling node is red and the left child is arbitrary. In this case, the exit is changed as follows, algorithm ends // because the sibling node is black, but the right child is red, the sibling node can be changed to the parent node color, and the right child and Node parent node becomes black // then left-handed along the node parent node, the node's sibling node is still black (for the right child of brother ), the height of the Child tree remains unchanged. // The height of the Child tree where the node is located increases by 1 because the node parent node is added to the Black node, so the black height on both sides is the same // (the original node subtree is 1 less than the black height of its brother subtree ), therefore, 5brothernode-> color = node-> parent-> color; node-> parent-> color = color: black; brothernode-> right-> color = color:: black; leftrotate (tree, node-> parent); // the problem has been solved. Point the node to the root node and exit the circular node = tree ;}} else {// the opposite of the sibling node. The principle is the same as that of brothernode = node-> parent-> left; // Case 1: sibling If the younger brother node is red and the sibling node is black, 2if (brothernode-> color = color: Red) {node-> parent-> color = color :: red; brothernode-> color = color: black; rightrotate (tree, node-> parent); brothernode = node-> parent-> left;} // Scenario 2: black if (brothernode-> left-> color = color: Black & brothernode-> right-> color = color: black) {// case 2.1: The left child of the sibling node is black, and the right child is black brothernode-> color = color: red; node = node-> parent ;} else {If (brothernode-> le FT-> color = color: Black) {// case 2.2: The left child of the sibling node is black and the right child is red, convert this situation to 2.3brothernode-> right-> color = color: black; brothernode-> color = color: red; leftrotate (tree, brothernode ); brothernode = node-> parent-> left;} // case 2.3: The left child of the sibling node is red and the right child is arbitrary. In this case, the exit is changed as follows, algorithm ends brothernode-> color = node-> parent-> color; node-> parent-> color = color: black; brothernode-> left-> color = color :: black; rightrotate (tree, node-> parent); // the problem has been solved. So that the node points to the root node and exits from the loop node = tree ;}} node-> color = color: black ;} /** delete a red/black tree node * The main time consumed is to traverse the successor node in sequence in obtaining the deleted node. the time complexity is O (lgn) * The time complexity for maintaining the red/black tree properties is O (lgn) * so the total time complexity is O (lgn) */rbtnode * rbdelete (rbtree * & tree, rbtnode * node) {If (! Tree |! Node) return; rbtnode * delnode = NULL; If (node-> left = nilnode | node-> right = nilnode) {delnode = node ;} else {delnode = rbtreesuccessor (node);} rbtnode * fillnode = NULL; If (delnode-> left! = Nilnode) {fillnode = delnode-> left;} else {fillnode = delnode-> right;} fillnode-> parent = delnode-> parent; if (delnode-> parent = nilnode) {tree = fillnode;} else {If (fillnode = fillnode-> parent-> left) {fillnode-> parent-> left = fillnode;} else {fillnode-> parent-> right = fillnode;} If (delnode! = Node) {node-> key = delnode-> key;} // If the deleted node is black, some black heights in the tree are reduced by one, properties must be maintained. 5 // Add a black node if (delnode-> color = color: Black) {rbdeletefixup (tree, fillnode) to the node-related path );} return delnode ;}
Chapter 2 Introduction to Algorithms