Finally, we will explore the red and black tree deletion algorithm, compared to the insertion operation, it is more complex situation. So it's easy to get into the south wall, we need to use the idea of conversion and transformation (remember the four ways of thinking in high school math, as applicable here), by raising the change, the red black tree mapped into a b- tree, and stand in the latter angle, in turn to understand the former principle. But we need to be more concerned with refactoring operations, which are a series of rotations and fixes, and in the process note that the number of refactoring Times is $o\left (1 \right) $ level .
This delete operation is quite complicated ... It can be said that the tedious to the point of disgust, you prepare for the psychological. can be next to prepare a plastic bag or a basin or something, save no place ...
First, some auxiliary functions are given to facilitate subsequent use.
Position Searchin (Position V,inte,position& parent) {//returns a reference to the parent node pointer, because subsequent left values if(!v | | (E = = V->value))returnV//recursive base, if a direct hit or not present returnsParent=v;//The general situation is to write down the current node and then go deep into the layer. returnSearchin ((E < v->value? v->left:v->Right ), E, parent);//The return value points to the hit node, which is the parent. Position Search (intTarget,redblacktree T) { returnSearchin (T, Target, p);} Position Getparentof (inte) {Search (E, FIR); returnp;}//since I write a struct that has no parent pointer, it is obtained by this function equivalent. Position getparentof (Position T) {//get the parent node of TSearch (t->value, fir); returnp;}intIslchild (Position p) {//determine if P point is the left child of a node if(Getparentof (p)! = Fir && getparentof (p)->left = =p)return 1; Else return 0;} Position& Fromparentto (Position p) {//returns a pointer from the father of a node if(Getparentof (p) = = Fir)returnP//handling P is the root of the situation if(Getparentof (P)->left ==p)//P is leftchild returnGetparentof (P)Left ; Else returnGetparentof (P)Right ;}
The removal of red and black trees is similar to BST Delete, from top to bottom (see the previous two fork tree implementation of the article), if the deleted node x is not a leaf, we will consider its direct successor (the smallest element on the right subtree) instead, so that X has a right child, or x is shifted to the position of the leaf, directly deleted. But there are some situations that need careful analysis because the red and black tree rules have other limitations.
For example, in this map node x after being deleted, it will be replaced by one of its descendants r, so that the nature of the red and black tree may not be able to continue to meet, verify that: First red black tree root, external nodes are not affected, but in this part there may be two consecutive red nodes, More importantly, the number of black nodes may change on the path where the node is deleted, and the fourth rule is not necessarily sufficient. There are also a large class of situations that are very easy to handle. Is the deletion of the node x and its replacement R there is a red (of course, it is not all red), such as up and down these two pictures of the situation.
in this case, as long as the replacement R is dyed black, it is ensured that rule 4 is not affected . The reason is that, from the tree structure before the delete operation is visible, this part contains a point pointing to the virtual edge of the red node, the previous article said that this kind of virtual edge is not affected by the black height, so after the R Black, it is equivalent to delete a virtual edge, so the black depth of all external nodes is not affected . removal of red leaves Fortunately, the problem is that if the leaves are black, then rule 4 is destroyed after deletion . The solution is to ensure that the leaves are always red during the removal from top to bottom. The following is a detailed analysis of this type of situation.
It's possible that the deleted node and the replacement are All Black,
In this case we also call the Double black, at this time the two nodes belong to the path of the black length will inevitably reduce a unit, thus inevitably against the red black tree fourth rule. And unfortunately, the concise approach is no longer valid. Before we give a new approach, we may need to understand from another perspective where the problem is. What angle? Of course, it's a B-tree. If both X and R are black, then in the corresponding 4-order B-Tree, X will become an internal node on its own
The node also overflows after the only key code has been removed. So our adjustment algorithm is not so much to fix the double black defect in the red and black tree as to fix the underflow defect in the B-tree. For this we need to examine two nodes: first, after deletion, the Father P of node r; In addition we need to examine the Brother S of node R in the original tree.
Let's first give you a way to delete a node somewhere, like BST.
Position removeAt (Position x) {Position temp; if(X->left && x->Right ) {Temp=findmin (x->Right ); X->value=temp->value; X->right=removeat (x->Right ); } Else{Temp=x; if(!x->left) x=x->Right ; Else if(!x->right) x=x->Left ; Free(temp); } returnx;}//returns the location of the deleted node
We'll dispose of them in 4 different cases.
First case:S is black and has at least one red child
Take a font as an example (left), and the rest of the case is symmetrical or similar. The adjustment method is to do the corresponding rotation and re-staining (right). The staining rule is: R continues to remain black, while T and P both dye black, and s will inherit the color of the previous root node p.
The 4 subtrees trees have the same black height, so all the properties of the red and black trees are restored after the adjustment. This conversion method is not accidental, but has a profound principle, is the B-tree. Next, let's turn to the B-tree angle to view the effect of this transformation.
As you can see, the double black flaw corresponds to a underflow, fortunately, the overflow node has a brother that is rich enough to eliminate underflow through rotation. Specifically, the Underflow node borrows a key from the father, and the father borrows a key code from the brother to fill the vacancy.
After this rotation, you can see that the underflow node has been repaired.
Next, restore the restored B-tree to the corresponding red-black tree, which is exactly the same as the transformation made directly on the red-black tree.
The new key will still inherit its predecessor's color, so it will never cause double blacks again in other locations. In this sense, the situation is relatively simple, and is reflected in the ability to complete only one rotation. In other words there is at least one red child. Since this is a simple situation, it is easy to know that it is more difficult for a child to be red. So what should we do?
second case: S is black , and two children are black. But P is red.
This situation is divided into two seed cases, the difference is that: at this time the parent node P is red or black, we first discuss the red situation.
First, the previous red-black tree was converted to the corresponding B-tree, and there was a underflow in this position. At this point we are not able to implement rotation adjustment, the reason is that at this time the Brother node S has no surplus, oneself already in the edge of overflow tempted, not enough to borrow any key code. Remember how to deal with it before, merge. Remove an element from the parent node and use it as an adhesive to combine left and right two nodes. The results of the fix are as follows:
Then transform back to the corresponding red-black tree, you can get a feasible adjustment scheme in the red-black tree:
Now stand in the red and black tree angle to observe the process, the result is equivalent to R to maintain the previous black, and s from black to red, while the p from red to black. So the above adjustment process in the red-black tree is exactly equivalent to a node in the B-tree merging with its brother to eliminate Underflow. And the restoration of this partial double black defect also means that the nature of the red and black tree can be restored in the global, once repaired, and thoroughly repaired.
The third case:S is black , and two children are black. and P is black.
In the same way, from the point of view of the B- tree, there will still be a underflow, and the same can only be eliminated by merging the sibling nodes.
The difference with the second case is that the element p at this point is alone as an internal node, so when the only element P is lent, the previous parent node is destined to overflow. That is, in this case, the double black defect may be propagated upward, or even continue uploading, until the last root. If you also use the old-fashioned repair, the most likely to happen logn times , that problem, the topology will change Logn times? That's not a good news.
In fact, just back to the red and black trees, you can visually understand this complex adjustment
again, there is no real change in the topological structure of the red-black tree after this adjustment. This means that the refactoring operation performed by the entire tuning process is not more than O (1) and isstill possible to implement. We have only one final case left below. that is, the Brother node s may not be black, but red.
Fourth case:S is red , children are black
Referring to the deletion of the ordinary BST , we only need to convert to a certain situation before the line, instead of a second. For this we need to stand again at the angle of the B-tree:
At this point P and s are combined into an internal node of a 3 branch, in which the B-tree simply swaps the color of S and p without any substantial structural adjustment. Of course, in the corresponding red-black tree, need to do a structural adjustment. Specifically, to rotate around the node p, while flipping the color of S and p.
We may be a little disappointed here, because the problem is not solved. For example, the original black height anomaly still exists. However, in fact this step is not meaningless, because the previous contradiction focus on the node R's brother s is red, now in the invisible R already has a black brother s ', so will inevitably jump out of the fourth situation, To the 3 cases discussed earlier . The better news is that the following may only be transferred to section 1 or 2 , and not to 3 . Since the first 3 is characterized by the parent node p must be black, after just the transformation,p has quietly turned red. In thecase of 1 and 2, the computational complexity is smaller because it does not spread upward. So after this adjustment, just one more round of recursion, the entire red and black tree will inevitably be completely repaired.
Then the specific code implementation is:
voidSolvedoubleblack (Position x) {//repair of double black defectsPosition p=getparentof (x);//father of R if(!p)return; Position Sibling= (x==p->left)? p->right:p->left;//brother of R if(Sibling-Col==black) {//Brother for BlackPosition temp=NULL; if(Sibling->left && sibling->left->col==Red) Temp=sibling->Left ; Else if(Sibling->right && sibling->right->col==Red) Temp=sibling->Right ; if(temp) {//Case 1: Black s have red childrenColor oldcol=p->col;//back up the color of the original root p,Fromparentto (P) =rotate (SIBLING->VALUE,P);//do a heavy balancePosition b=Fromparentto (P); //and dye The children of the new subtree black. if(B->left) b->left->col=Black; if(b->right) b->right->col=Black; b->col=oldcol;//New roots inherit the original color } Else{//Case 2, 3: Black s no red childsibling->col=red;//s turn redsibling->height--; if(P->col ==red)//Scenario 2P->col=Black; Else{//Scenario 3p->height--;//color remains, but black height minus 1Solvedoubleblack (P); }}} Else{//Case 4: Brothers for RedSibling->col=Black; P->col=red;//s turn black, p turns redPosition t= islchild (sibling)?sibling->left:sibling->Right ; Fromparentto (P)=rotate (sibling->value,p); } solvedoubleblack (x);}//the formal removal processvoidDelete (intE,redblacktree T) {Position X= Search (E, T);//x point to the node being deleted if(! X) printf ("%d not found!", E); Position R=removeAt (X); if(Getparentof (R) ==fir)//if it's a root node, dye it blackR->col=Black; if(r->col==red)//if R is red, just dye it black.R->col=Black; //The following situation: The original X (now R, because it was deleted, and then replaced) are blackSolvedoubleblack (R);}
It looks very ... Long Story There are some minor problems with this code, but it's enough to help us understand the removal process, which can be a pseudo-code for the time being. Originally thought not to put up, but the light look at the picture to understand, specious, so still look at the code implementation.
Now for a summary, here is a case analysis of the deletion:
The deletion time complexity of the red black tree will not exceed $o\left (\log n \right) $ for each delete operation at most, at most, at a constant time. Through the above summary can be found that the deletion of red and black trees at most only need to do $\log n$ the re-dyeing, and often several times the structural adjustment. This is also an important aspect of the red-black tree superior to the AVL tree. Remember, as mentioned at the beginning of the red-black tree, this feature is critical to the implementation of the persistence structure.