Before we learned the binary search tree, we found that in some cases its height is not very uniform, and sometimes degenerate into a long chain, so we refer to some "balanced" two-fork search tree. The red and black tree is a "balanced" two-fork search tree, which ensures that in the worst case, the time complexity of the basic dynamic set operation is O (NLGN) by attaching some constraints on the color bit and path at each node. The following summarizes the nature of the red-black tree, then analyzes the red-black tree insert operation and gives a complete code.
First give the red and black tree node definition:
#define RED 1 #define BLACK 0 /// red Black tree node definition, one more color field than the normal two-tree search tree typedef struct node{int key,color; /// definition 1 is red, 0 is black node *p,*left,*right; Node () {color=black; /// default node color is black }} *rbtree;
I. From the nature of the red and black trees
The red and black tree is a binary search tree, which adds a storage bit to each node to represent the node's color, which can be red or black. The red-black tree ensures that no path will be twice times longer than the other paths, and thus is approximately balanced, by constraining each node color on a simple path from the root to the leaf node. The following gives the red and black trees of the five "red-black Nature", these properties are behind the insertion and deletion of the basis.
1). Each knot is either red or black.
2). The root node is black.
3). Each leaf node is black.
4). If a node is red, its two sub-nodes are black.
5). For each node, the same number of black nodes are included on the simple path from the node to all of its descendant leaf nodes.
We set the number of black nodes on a simple path from a node x (without the node) to a leaf node to the black height of the node, as BH (x). Obviously, according to the nature of 5, the black height of each node is unique. Let us prove that the height of a red black tree containing n internal nodes is at most 2LG (n+1).
Prove that at least 2^BH (x)-1 internal nodes are included in the subtree with either node x as the root. We use the mathematical induction method to summarize the black height of x. If the black height of x is 0 or x is the leaf node, it is clearly correct. The black height of x is BH (x), the black height of the two subtrees trees is BH (x) or BH (x)-1 (depending on their own color), it can be concluded that the corresponding internal node is at least 2^ (BH (x)-1)-1, that is, X contains an internal node of at least 2^ (BH (x) 1) -1+2^ (BH (x) -1) -1+1=2^BH (x)-1, therefore proof. And now we have n nodes with a height of h, then according to the nature of 4, we can know that from the root to the leaf node (not including the root node) on any simple path is at least half of the nodes are black. Therefore the black height of the tree is at least h/2; Then there is the above conclusion n>=2^ (H/2)-1. H<=2LG (n+1). So we have proved that the dynamic set operation Search,minimum,successor can be completed in the time complexity of O (LGN).
Here is a picture of a red and black tree.
Two. Rotation operation
In the subsequent insertion and deletion process will be destroyed by the nature of the red and black tree sent, in order to maintain these properties, it is necessary to change some nodes in the tree color and pointer structure. The modification of the pointer structure is done by rotation, and the rotation is divided into left and right; in the insert and delete operations, these two actions occur more than once, so let's start by analyzing them.
The images for these two operations are as follows:
As can be seen from the image, left-and right-handed operations are not very complex. Let's take a left-hand example to parse: we assume that the right son of node X is Y. The premise of the left is that the node must have a right son. The result of the left-hand is: Y instead of x,x to become Y's son, X becomes the left son of Y, Y's left son becomes X's right son. The following is a specific code implementation:
//L: For nodes with any right child, can be/// pass in a reference to the node pointer to select/// rotate after X's right child y replaces x, and x becomes Y's left child, Y's left child becomes X's right child/// The following code is the completion of these three piecesvoidLeftrotate (Rbtree x) {Rbtree y=X -Right///y represents the right child of XX -Right=Y -Left///First step: Left child with X's right child set to Y if(Y -Left!=Nul) y -Left -P=X Y -P=X -P///Change parent node of y to x if(x -P==Nul)////second step: Y alternative X, need to discuss the situationRt=Y///x is the root node, set y as the root node . Else if(x==X -P -left) x -P -Left=Y/// change y to x parent node's left child ElseX -P -Right=Y/// change Y to the right child of X parent nodeY -Left=X///Step three: Left child with X set to YX -P=Y;}
Right-hand and left-handed are similar, the code is as follows:
//Right: Any node with left child can be/// pass in a reference to the node pointer to be right-handed/// rotation after node x is replaced by left child y, X becomes the right son of Y, Y's right child becomes X's left childvoidRightrotate (Rbtree x) {Rbtree y=X -Left///y represents the left child of XX -Left=Y -Right///First step: X's left child changed to Y's right child if(Y -Right!=Nul) y -Right -P=X Y -P=X -P///Change parent node of y to x if(x -P==Nul)////second step: Y alternative X, need to discuss the situationRt=Y///x Original root node, specify Y as the new root node . Else if(x==X -P -left) x -P -Left=Y/// Change x parent node's left child to Y ElseX -P -Right=Y/// Change x parent node's right child to YY -Right=X///Step three: Change the right node of Y to xX -P=Y;}
Three. Insert Detailed
The difficulty in understanding the insertion and deletion of red and black trees is that there are too many things to do after we change a node. We also get a basic discussion about how to categorize all the situations and then understand why the code is written like this. In fact, as long as the "Introduction to the algorithm" in the case to understand clearly, the operation of the red and black tree is not difficult to understand. The following applies the idea of classification discussion to understand the red-black tree insert operation.
First of all, let's give the code for the insertion section first:
/// red-black tree InsertThe ///rb insertion function is just a little bit different from the normal BST insertion function.///// we replaced the original null with the NUL node, and then the newly added node was dyed red.//// then call the Rbinsertfixup function to adjust so that the nature of the red and black tree is not destroyedvoidRbinsert (int key) {Rbtree Z=NewNode Z -Color=RED; Z -Key=Key Z -P=Z -Left=Z -Right=Nul; Rbtree y=Nul; Rbtree x=Rt while(x!=Nul)///To find the insertion point of Z according to the nature of the binary search tree{y=Xif(Z -Key<X -Key) x=X -LeftElseX=X -Right } Z -P=Yif(Y==Nul)/// The root node is insertedRt=ZElse if(Z -Key<Y -Key) y -Left=ZElseY -Right=Z Rbinsertfixup (z);/// inserting red nodes may violate certain properties of red and black trees, call adjustment function to adjust}
From the code point of view, the insertion here is not much different from the insertion of a two-fork search tree. Just add an Insert adjustment function at the end: Rbinsertfixup (). The reason to add an adjustment function is because we insert a node and dye it red to cause a property of the red-black tree to be violated, so we need to focus on what is going to be a violation of that nature, and then we do this in the adjustment function to restore the nature of the situation.
First we set the inserted node as z, and the z will be dyed red after insertion. If the parent node of z is black, it does not violate any nature and does not need to be adjusted. So we just need to discuss the case where the parent node of z is red. And then, in this case, only the nature of 4 is bound to be violated, and then we then press the parent node of Z is the left son of its grandfather node or the right son classification (these two situations are not essential differences, just a little bit different in coding), below we only discuss the parent node of Z is the left son of its grandfather node. We then set the tertiary node of z (that is, the sibling node of the z parent node) as Y. Under these conditions, we will discuss the following categories:
1) Condition 1:y color is red
At this point the grandfather node must be black (because the red-black tree was not violated before inserting the node). In this case, we can dianran the parent knot of z into Black, Z's tert-knot dianran into black, and Z's grandfather knot Dianran Red; After this, Z's grandfather node is definitely a legitimate red-black tree, but Z's grandfather node may violate the nature of 4, which is equivalent to the ascent of z in the tree by two layers (z=z-> P->P). In this case, if the parent node of the new z is black, it exits the loop (if the new z is the root node it must exit, the root nodes may be dyed red, so the root node needs to be dyed black after exiting the loop).
This situation is as follows:
2): The color of y is black
In the case where Y's color is black, we classify the left child or right child by Z as the parent node.
(1). Z is the left child of its parent node
At this point we can dianran the parent knot of z into Black, Z's grandfather Knot Dianran Red, and then the grandfather node of Z has a right-spin recovery of 4, and does not violate the other nature (for this we can clearly see if we draw one ourselves).
(2). Z is the right child of its parent node
This is obviously done by pointing Z to the parent node of Z and then turning the z one at a time to the case (1).
These two situations are as follows: where the picture on the case two corresponds to our here (2), the situation three is for us here (1)
In general, as long as Y is black, we can exit the loop; The two cases above are just small transformations of the internal structure.
In fact, the red and black tree insert operation is mainly in violation of the nature of 4, the process of adjusting the function is divided into two types of conditions on the nature of 4 to adjust the process, is not very complex. The above gives the case that the parent node of Z is the left son of his grandfather, in fact the situation of the right son is basically the same (see Code for details).
Another puzzling point is that the red-black tree's code is heavily used in the form of z->p->p, but does this really cause null pointer exceptions? In this paper, the introduction of this algorithm is more detailed argumentation. Here I would like to briefly say my opinion: first, when inserting the root node, is not into the loop, so do not reference z->p->p. Then, after inserting the root node, the grandparent nodes of each node outside the root node are always there, so the first reference does not go wrong. The problem can only occur if z is moved up in the tree (corresponding to case 1) and may move up to a location where the parent node does not have a parent node, which leads to the danger of null pointers, but we should note that this position is only possible with root nodes, and if z moves to the root node, Then it will exit the loop because the parent node of the root is black! There is no longer a reference to this form of z->p->p. So there's no need to worry about a null pointer exception to the Z->P->P reference.
Here is the code for the red-black Insert Adjustment function Rbinsertfixup () function, which corresponds to the three cases we discussed above:
/// Red black tree Insert Adjustment function//// We will insert knot Dianran into red, may violate the nature of 4, so to adjust///The process of adjustment is in fact classified according to different situations to discuss, the process of continuous conversion///final turn into a condition that can be recovered by dyeing and rotationvoidRbinsertfixup (Rbtree z) {/// in the following code, the Z-node is always in violation of the node of Nature 4. while(Z -P -Color==RED)///x is red, its parent node is also red, indicating that the nature of 4 is violated, to continue to adjust{/// The following procedure is classified as the left child or right son of X->p's grandfather node. if(Z -P==Z -P -P -Left/// parent node is the left child of his grandfather's knot .{Rbtree y=Z -P -P -Right/// represents the tertiary node of Z /// below to sort by the color of Y if(Y -Color==RED) {if y is red and Z's grandfather node must be black, then we pass the following dyeing process ///In the case of guaranteed black height (nature 5), Z is moved up two layers in the tree, Z=z->p->pZ -P -Color=BLACK; Y -Color=BLACK; Z -P -P -Color=RED; Z=Z -P -P///If you move up to the root node or a node with a parent node that is not red, you can end the loop.}Else /// tert-junction is black{/// At this point, we will discuss the classification according to the left child or right child whose parent node is Z . if z is the left child, it can be directly restored by dyeing and right-handed. if z is the right child, you can turn left to the right child. if(Z==Z -P -right) {Z=Z -P Leftrotate (z);/// Direct left-handed}/// re-dyed, then right-handed to restore propertiesZ -P -Color=BLACK; Z -P -P -Color=RED; Rightrotate (Z -P -p); } }Else/// Father node is the right child of the grandfather's knot .{Rbtree y=Z -P -P -Left/// tert-Nodal points if(Y -Color==RED) {Z -P -Color=BLACK; Y -Color=BLACK; Z -P -P -Color=RED; Z=Z -P -P }Else{// Right son can be directly left-handed, re-color restore properties /// left son can turn right into right son and then deal with if(Z==Z -P -left) {Z=Z -P Rightrotate (z); } Z -P -Color=BLACK; Z -P -P -Color=RED; Leftrotate (Z -P -p); } } }/// to dye the root node black, is a necessary step; handling two cases ///1. The first insertion of the root node is dyed red . ///2. And in the above loop, the root node may be dyed red .Rt -Color=BLACK;}
Next: Introduction to the algorithm learning – Red and black tree detailed removal
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Introduction to Algorithmic learning---red and black tree Chenghou insertion (C language Implementation)