"Data structure and algorithm 05" Red-Haishi (read the package understand ~) __java

Source: Internet
Author: User
Tags comparable

(Friendship hint, red-Haishi is based on the binary search tree, if the two-fork search tree does not understand, you can look at: two fork search tree)

As you can see from the Analysis in section 4th, a binary search tree is a good data structure that can quickly find a given keyword and can quickly insert and delete data items. But the binary search tree has a very troublesome problem, if the tree is inserted in random data, the execution is good, but if the inserted in order or reverse the data, then the two-fork search tree execution speed becomes very slow. Because when the insertion of numerical order, the binary tree is unbalanced, in a line, in fact, become a linked list ...         Its ability to quickly find, insert, and delete specified data items is lost. In order to search for a tree at a faster time O (Logn), it is necessary to ensure that the tree is always balanced (or at least mostly balanced), which means that the number of descendants on the left side of each node in the tree and the number of descendants on its right should be roughly equal. Red-Haishi is such a balance tree, for a data item to be inserted, the insertion routine to check will not destroy the characteristics of the tree, if destroyed, the program will be corrected, according to the need to change the structure of the tree, so as to maintain the balance of the tree. So what are the characteristics of red-black trees? 1. Characteristics of red-Haishi

It has two main features: 1. Nodes have colors; 2. In the process of inserting and deleting, follow the rules that keep these colors in different permutations. First, the first feature is well resolved, and a data field, such as a Boolean variable, is used in the node class to represent the color information of the node. The second feature is more complex, the red-black tree has several of its rules, and if these rules are followed, then the tree is balanced. The main rules of red-Haishi are as follows:

1. Each node is either red or black;

2. The root node is always black;

3. If the node is red, its child nodes must be black (not necessarily);

4. Each path from the root node to the leaf node or the empty empty node must contain the same number of black nodes (that is, the same black height).

It is not accidental that the nodes inserted in the red-black tree are red, because inserting a red node is less likely than inserting a black node against the red-black rule. The reason: Inserting a black node always changes the black height (violating rule 4), but only half the chance of inserting a red node violates rule 3. In addition, violating rule 3 is more likely to be amended than violating rule 4. When inserting a new node, it may break the balance, so how does red-Haishi fix it?

2. Correction of the balance

The red-black tree mainly modifies the balance in three ways, changing the color of the nodes, the left and the right rotation. This looks a little abstract, and we'll introduce them separately. 1. Discoloration

Changing the color of a node is easier to understand because it violates rule 3. Suppose there is now a node E, then insert Node A and node s, Node A in the left child node, s in the right child node, now is balanced. If you plug another node at this point, there is an imbalance, because the nodes of the red node must be black, but the newly inserted node is red. So it's time to change the color of the nodes. So we're going to change the two children of the root from red to black (as for why we're all changing, we'll explain this in detail when we insert them) and turn the parent node from black to red. You can use the following schematic to indicate:


2. Left rotation

A left-spin operation is usually used to rotate a red link to the right to the left link. The schematic diagram is as follows:


I have a very Meng Meng dynamic diagram, you can easily understand:


3. Right rotation

Right rotation can be reversed, here no longer repeat, directly to see schematic:


Of course, the right spin also has a dynamic picture of the eruption:

Here mainly introduced the red-Haishi to balance three kinds of correction way, everybody has a perceptual understanding, so when this amendment. What kind of correction should you use? This is going to be the next issue we're going to explore.

3. Red-Haishi operation
The basic operation of red-Haishi is to add, remove, and rotate. When you add or remove red-Haishi, you may be able to break its balance and use the rotation to correct it. We first introduced the red-Haishi node, and then to the left and right rotation of the specific implementation of the analysis, and finally we discuss the next red-Haishi of the specific operation.
1. Red-Haishi nodes

Red-Haishi is the improvement of the two-fork search tree, so its node and the two-fork search tree is almost the same, but it added a Boolean variable to represent the color of the node, specifically see Rbnode<t> class:

public class Rbnode<t extends comparable<t>>{
	boolean color;//colors
	T key;//keyword (key value)
	Rbnode<t > left; Zoozi node
	rbnode<t> right;//right-hand child node
	rbnode<t> parent;//parent node public
	
	rbnode (T key, Boolean color, Rbnode<t> parent, Rbnode<t> left, rbnode<t> right) {
		this.key = key;
		This.color = color;
		This.parent = parent;
		This.left = left;
		This.right = right;
	
	Public T Getkey () {return
		key;
	}
	
	Public String toString () {return
		"" + key + (This.color = = RED?) R ":" B ");
	}

2. The specific implementation of the left spin

Above the concept of L has a perceptual understanding, here will no longer repeat, we from the following code in conjunction with the above schematic diagram, to explore the specific implementation of L:

/************* to the red-black tree node x left-hand operation ******************//* left-spin diagram: To the node X to the left-p p *       //* x Y */\/* LX y-----> x ry */\/\ * ly ry LX ly * Left spin done three things: * 1. Assigns the left child node of Y to the right child node of X and assigns x to the parent node of the Y Zoozi node (Y Zoozi node is not empty) * 2. Assigns X's parent node p (not null) to the parent of Y, while the child nodes of P are updated with Y (left or right) * 3. Set the left child node of Y to X, set X's parent node to Y/private void Leftrotate (Rbnode<t> x) {//1. Assigns the left child node of Y to the right child node of X and assigns x to the parent node of the Y Zoozi node (Y Zoozi node is not empty) rbno
	De<t> y = x.right;
	
	X.right = Y.left;
	
	if (y.left!= null) y.left.parent = x; 2.
	
	Assigns X's parent node P (non-null time) to the parent node of Y, while the child nodes of P are updated with Y (left or right) Y.parent = x.parent; if (x.parent = = null) {this.root = y;//If X's parent node is empty, set Y to parent node} else {if (x = = X.parent.left)//If X is Zoozi node X.parent.lef t = y; You also set Y to Zoozi node else X.parent.right = y;//otherwise set Y to right child node}//3.
	Set the left child node of Y to X and set X's parent node to y y.left = x;		
X.parent = y; }

3. Right Rotation concrete Realization

Above the concept of the right to spin has a perceptual understanding, here also no longer repeat, we from the following code in conjunction with the above schematic diagram, to explore the specific implementation of the right rotation:

/************* to the red and black tree node y to the right rotation ******************/
 /* Left spin diagram: The node y to the right
 * *        p                   p
 *       /                   /
 *      y                   x
 *     /\                 /\
 *    x  ry   ----->      lx  y
 *   /\                     /
 * LX  rx Rx                   ry
 * Do three things:
 * 1. Assigns the right child node of X to the left child node of Y and assigns y to the parent node of the X Right child node (the X right child node is not empty)
 * 2. Assigns Y's parent node p (not null) to the parent of X, while updating the child nodes of P to X (left or right)
 * 3. Set the right child node of x to Y and set the parent of Y to x
/private void Rightrotate (Rbnode <T> y) {
	//1. Assigns the left child node of Y to the right child node of X and assigns x to the parent node of the Y Zoozi node (Y Zoozi node is not empty)
	rbnode<t> x = y.left;
	Y.left = x.right;
	
	if (x.right!= null) 
		x.right.parent = y;
	
	2. Assigns X's parent node P (non-null time) to the parent node of Y, while the child nodes of P are updated with Y (left or right)
	x.parent = y.parent;
	
	if (y.parent = = null) {
		this.root = x;//If the parent of x is empty, set Y to parent node
	} else {
		if (y = = y.parent.right)//If X is Zoozi node 
  y.parent.right = x; You will also set Y to Zoozi node
		else
			y.parent.left = x;//Otherwise set Y to the right child node
	}
	
	//3. Set the left child node of Y to X and set X's parent node to y
	x.right = y;
	y.parent = x;		
}
4. Insert Operation

After analyzing the main rotation operations in the red-black tree, we began to analyze common inserts, deletions, and so on. This first analyzes the insert operation. Since red-Haishi is an improvement on the two-fork search tree, the first half of the insert operation is the same, that is, find the location to insert, then insert the node, first look at the first half of the inserted code:

/*********************** to the red-black tree Insert node **********************/public void Insert (T key) {rbnode<t
	> node = new rbnode<t> (key, RED, NULL, NULL, NULL);
if (node!= null) insert (node); 
	//Insert the node into the red and black tree, this process is the same as the two-fork search tree, private void Insert (rbnode<t> node) {rbnode<t> current = null;//Represents the parent node of the last node rbnode<t> x = this.root; Used to search downward for the//1.
		Locate the inserted location while (x!= null) {current = X;
		int cmp = Node.key.compareTo (X.key);
		if (CMP < 0) x = x.left;
	else x = x.right; } node.parent = current; A location was found, and currently current is//2 as the node's parent.
		Next, determine whether node is plugged into the left or right child node if (current!= null) {int CMP = Node.key.compareTo (Current.key);
		if (CMP < 0) current.left = node;
	else Current.right = node;
	else {this.root = node; }//3.
Trim it to a red-black tree Insertfixup (node); }
This and two fork search tree implementation of the same idea, here no longer repeat, mainly to see the method inside the last step insertfixup operation. Because the insertion may lead to the imbalance of the tree, the Insertfixup method is mainly divided into discussion, analysis when discoloration, when left, when the right rotation. We first analyze the concrete situation theoretically and then look at the concrete realization of the Insertfixup method.

If this is the first time, because the original tree is empty, it will only violate red-Haishi rule 2, so as long as the root node black can be, if the insertion node of the parent node is black, it will not violate the red-Haishi rules, do not need to do anything, but encountered in the following three cases, we will begin to change color and rotation:

1. The parent node of the insertion node and its Uncle node (another child node of the grandparent node) are red;

2. The parent node of the insertion node is red, the Uncle node is black, and the insertion node is the right child node of its parent node;

3. The parent node of the Insert node is red, the Uncle node is black, and the Insert node is the left child node of its parent node.

Let's take a look at each of these three cases and then give the implementation code.

For Case 1: The parent node of the Insert node and its Uncle node (another child node of the grandparent node) are red . At this point, there must be a grandparent node, but do not know whether the parent node is its left child or right child node, but because of symmetry, as long as we discuss one side of the situation, the other is naturally corresponding. Here, consider the case where the parent node is the left child node of the grandparent node, as shown in the following image:

In this case, we have to do is: the current node (4) of the parent node (5) and Uncle Node (8) Black, the grandfather node (7) to red, as shown in the upper right figure. Then point the current node to its grandparent node, and start the algorithm again from the new current node (see below for a specific procedure). So the right picture becomes 2.

For Case 2: The parent node of the Insert node is red, the Uncle node is black, and the Insert node is the right child node of its parent node . We have to do is: the current node (7) of the parent node (2) as a new node, the new current node for the pivot to do the left-side operation. This is done as shown in the lower left image, so the bottom left image becomes 3.

For Case 3: The parent node of the Insert node is red, the Uncle node is black, and the Insert node is the left child node of its parent node . We have to do is: the current node of the parent (7) black, the grandfather node (11) Red, in the grandfather node for the fulcrum of the right rotation operation. Finally, the root node is blackened, and the entire red-Haishi is restored to balance, as shown in the upper-right image. At this point, the insert operation completes.

And we can see that if it's happening from the 1 start, is bound to go through the situation 2 and 3, which means that this is a whole process, of course, in practice may not necessarily be from the situation 1, if from the beginning of the situation 2, then go a situation 3 can complete the adjustment, if directly adjust the situation 3, Then neither of the first two cases needs to be adjusted. Therefore, the relationship between discoloration and rotation can be expressed as: discoloration-> l-> right.

So far, we've done all the inserts. Let's take a look at the specific implementation of the Insertfixup method (which can be combined with the above analysis to make it more benefit and understandable):

private void Insertfixup (rbnode<t> node) {rbnode<t> parent, gparent;//define parent node and grandparent node//needs trimming Condition: parent node exists, and parent node color Is red while ((parent = parentof (node)!= null) && isred (parent) {gparent = Parentof (parent);//Get grandparent node//if Father The node is the left child node of the grandparent node, and the following else is the opposite if (parent = = Gparent.left) {rbnode<t> uncle = gparent.right;//Get Uncle Node//cas
				E1: Uncle node is also a red if (Uncle!= null && isred (uncle)) {setblack (parent); The parent node and Uncle Node are painted black setblack (uncle); Setred (gparent); The grandfather node painted red nodes = gparent; Place the position at the grandparent node continue;
				Continue while, re-judge}//case2: Uncle node is black, and the current node is the right child node if (node = parent.right) {leftrotate (parent), or left at the parent node rbnode<t> tmp = parent;
				Then the parent node and its own exchange, for the next right to make preparation parent = node;
			node = tmp;
			//CASE3: Uncle node is black, and the current node is Zoozi node Setblack (parent);
			Setred (gparent);
		Rightrotate (gparent);
			
			else {//If the parent node is the right child node of the grandparent node, the exact opposite of the above, the rbnode<t> uncle = Gparent.left; Case1: Uncle node is also red if (uncle != Null & isred (uncle)) {setblack (parent);
				Setblack (uncle);
				Setred (gparent);
				node = gparent;
			Continue
				//case2: Uncle node is black, and the current node is Zoozi node if (nodal = parent.left) {rightrotate (parent);
				rbnode<t> tmp = parent;
				parent = node;
			node = tmp;
			//CASE3: Uncle node is black, and the current node is the right child node Setblack (parent);
			Setred (gparent);
		Leftrotate (gparent);
}///Set the root node to black Setblack (this.root); }

5. Delete operation

The above discussed the red-Haishi insert operation, the next discussion delete, red-Haishi Delete and two fork Lookup tree deletion is the same, but deleted after a balance of repair. Let's recall the deletion of the binary search tree (you can also read this blog directly: two fork search tree):

1. If you want to delete a node without a child node, then directly delete it;

2. If the node to be deleted has only one child node, delete it directly and replace it with its child nodes;

3. If the node you want to delete has two child nodes, this is complicated: the first choice is to find its successor node, then handle the relationship between the successor node and the parent node of the deleted node, and finally deal with the relationship between the child nodes of the successor node and the child nodes of the deleted node. Each step will also have a different situation, we combine the following code analysis can be made clear, of course, if you have understood the two-fork search tree, it naturally can understand, here is not to repeat.

Let's take a look at the code and comments for the delete operation:

/*********************** Delete nodes in the red-black tree **********************/public void Remove (T key) {rbnode<t> node;
if (node = search (root, key))!= null) remove (node);
	private void Remove (rbnode<t> node) {rbnode<t> Child, parent;
	
	Boolean color; 1. The deleted node "left and right child nodes are not NULL" if ((Node.left!= null) && (node.right!= null)) {//First find the successor node of the deleted node and use it to replace the location of the deleted node RBNODE&L T
		t> Replace = node; 1).
		Gets the successor node replace = Replace.right;
		
		while (replace.left!= null) replace = Replace.left; 2). The relationship between "successor Node" and "Parent node of deleted node" is handled if (Parentof (node)!= null) {//the node to be deleted is not the root node if (nodes = parentof (node). left) Parentof (no
			DE). left = replace;
		else Parentof (node). right = replace;
		else {//otherwise this.root = replace; }//3). Handles the relationship between child nodes of successor nodes and children of deleted nodes child = Replace.right;
		The successor node certainly does not have a left child node.
		Parent = Parentof (replace);
		color = colorof (replace);//Save the successor node's colors if (parent = node) {//successor node is the child node of the deleted node parent = replace; else {//otherwise if (CHILD!= NULL) setparent (child, parent);
			Parent.left = child;
			Replace.right = Node.right;
		SetParent (node.right, replace);
		} replace.parent = Node.parent; Replace.color = Node.color;
		Keep the original position of the color replace.left = node.left;
		
		Node.left.parent = replace;
		if (color = black) {//4. If the successor node is removed in black, the removefixup (child, parent) is trimmed and/or the child and parent of the successor node is passed in} node = null;
	Return }
}
Below we mainly look at the final removefixup operation in the method. Since remove may cause the imbalance of the tree, the Removefixup method is mainly divided into discussion, analysis when discoloration, when left, when the right rotation. We also first analyze the specific situation theoretically, and then look at the concrete implementation of the Removefixup method.

As you can see from the code above, when you delete a node, it is filled with its successor, and the successor node is set to the same color as the deleted node, so the location where you delete the node will not break the balance. It is possible to break the balance by the original position of the successor node, as the successor node takes away and the original position structure changes, which leads to unbalanced appearance. So the parameter passed in the Removefixup method is also the child node and the parent node of the successor node.

To facilitate the narrative below, we now agree that the child nodes of the successor node are called the "current node."

After the delete operation, if the current node is a black root node, then there is no action, because it does not break the tree's balance, that is, there is no violation of the red-Haishi rules, which is well understood. If the current node is red, indicating that the successor node that has just been removed is black, then no matter what color the parent of the successor node is, we can just paint the current node black, and the red-Haishi balance can be restored. But in the following four cases, we need to restore the red-Haishi balance by changing color or rotation.

1. The current node is black and the sibling node is red (then the child nodes of the parent node and sibling node are definitely black);

2. The current node is black, and the sibling node is black, and the two child nodes of the sibling node are black;

3. The current node is black and the sibling node is black, and the left child node of the sibling node is red and the right child node is black;

4. The current node is black, and the sibling node is black, and the right child node of the sibling node is red, and the left child node is any color.

In the above four cases, we can see that 2,3,4 is actually "the current node is black, and the sibling node is black" of the three subsets, etc, can be reflected in the program. Now let's assume that the current node is a Zoozi node (or, of course, the right child node, as opposed to the Zoozi node, we'll talk about it), and address each of the above four situations:

For Case 1: The current node is black and the sibling node is red (then the child nodes of the parent and sibling nodes must be black). As shown in the following image: A node represents the current node. In this case, we are going to do the following: The parent node (B) is painted red, the sibling node (D) is blackened, then the parent (B) of the current node (A) is left as the pivot point, then the current node's sibling becomes black (naturally it is converted into a public feature of the 2,3,4), as shown in the following illustration:


For Case 2: The current node is black, and the sibling node is black, and the two child nodes of the sibling node are black . As shown in the lower left figure, a represents the current node. In this case, we have to do is: the Brother Node (D) Red, the current node point to its parent node (B), its parent node to the current node of the grandparent node, continue the new algorithm (see the following program), do not need to rotate. This turns out to be the case shown in the lower right image:

For Case 3: The current node is black, and the sibling node is black, and the left child node of the sibling node is red and the right child node is black . As shown in the lower left figure, A is the current node. In this case, we have to do is: the current node of the Brother node (D) Red, the sibling node of the left sub-node (C) black, and then the sibling node as a fulcrum for the right rotation operation. Then the sibling node becomes black, and the right child node of the sibling node turns red (condition 4). As in the bottom right figure:


For Case 4: The current node is black, and the sibling node is black, and the right child node of the sibling node is red, and the left child node is any color . As shown in the following picture: A is the current node, in this case, we have to do is: the Brother Node (D) painted the color of the parent node, and then the parent node (B) black, the sibling node of the right child node (E) black, and then the current node of the parent node for the pivot to do the left rotation operation. At this point, the removal of the repair algorithm is over, and finally the root node is painted black.


We can see that if it is starting from 1, it may be one of the 2,3,4: if it is 2, it is impossible to see 3 and 4, and if it is 3, it will inevitably lead to a situation of 4; If 2 and 3 are not, it must be 4. Of course, in practice it may not necessarily happen from 1, depending on the situation.

So far, we have completed all the deletions. Let's take a look at the specific implementation of the Removefixup method (which can be combined with the above analysis to make it more benefit and understandable):

node represents the nodes to be modified, that is, the child nodes of the successor node (because the successor node is moved to the location where the deletion node was removed) private void Removefixup (Rbnode<t> node, rbnode<t> parent) {
	
	Rbnode<t> Other; while (node = null | | isblack (node) && (node!= this.root)) {if (Parent.left = = node) {//node is a Zoozi node, the following else is Just the opposite of other = Parent.right;
				Node's sibling node if (isred (other)) {//case1:node's sibling is the Red Setblack (other);
				setred (parent);
				Leftrotate (parent);
			other = Parent.right; 
					The sibling of//case2:node is black, and the other two subnodes are black if ((Other.left = null | | isblack (other.left)) && (Other.right = null | | isblack (other.right)))
				{setred (other);
				node = parent;
			Parent = parentof (node);
					else {//case3:node Brother node is black, and the left child node of other is red and the right child node is black if (other.right = null | | isblack (other.right)) {
					Setblack (Other.left);
					Setred (other);
					Rightrotate (other);
				other = Parent.right; The Brother node of//case4:node is black, and the right child of other is red, and the left child node is any color setcolor (other, COlorof (parent));
				Setblack (parent);
				Setblack (Other.right);
				Leftrotate (parent);
				node = this.root;
			Break
			
            } else {//symmetric with the above = Parent.left;
                if (isred) {//Case 1:node brother other is red setblack (other);
                setred (parent);
                Rightrotate (parent);
            other = Parent.left; if (Other.left==null | | isblack (OTHER.LEFT)) && (Other.right==null | | isblack (othe
                R.right)) {//Case 2:node brother other is black, and the other two sub nodes are Black setred (other);
                node = parent;
            Parent = parentof (node); else {if (Other.left==null | | isblack (OTHER.LEFT)) {//Case 3:node brother other is black, and  
                    And the left child node of other is red, and the right child node is black.
                    Setblack (Other.right);
                    Setred (other);
                 Leftrotate (other);   other = Parent.left; 
                //Case 4:node brother other is black, and other's left child node is red, right child node any color setcolor (other, colorof (parent));
                Setblack (parent);
                Setblack (Other.left);
                Rightrotate (parent);
                node = this.root;
            Break
{}} if (Node!=null) setblack (node); }

4. Complete source

Finally analyzed all the inserts and deletes the operation the thing. In addition, red-Haishi also some other operations, such as: Find a specific value, traverse, return the most value, destroy the tree and other operations I will be placed in the source code for everyone to appear, see below the red-Haishi complete codes.

Package tree; /** * @description Implementation of Red-black tree by Java * @author eson_15 * @param <T> * @date 2016-4-18 19: 27:28 */public class Rbtree<t extends Comparable<t>> {private rbnode<t> root;//root node private static Final Boolean RED = false;
	
	Define red black tree flag private static Final Boolean black = true; Inner class: Node class public class Rbnode<t extends comparable<t>>{Boolean color;//Colors T key;//keyword (key value) rbnode< T> left; Zoozi node rbnode<t> right; Right child node rbnode<t> parent; Parent node public Rbnode (t key, Boolean color, rbnode<t> parent, rbnode<t> left, rbnode<t> right) {T
			His.key = key;
			This.color = color;
			This.parent = parent;
			This.left = left;
		This.right = right;
		Public T Getkey () {return key; Public String toString () {return "" + key + (This.color = = RED?)
		R ":" B ");
	} public Rbtree () {root = null; } public rbnode<t> Parentof (rbnode<t>node) {//Get the parent node return node!= null node.parent:null; public void SetParent (rbnode<t> node, rbnode<t> parent) {//Set parent node if (node!= null) Node.parent = PA
	Rent
	public boolean Colorof (rbnode<t> node) {//Get the color of the node!= null? Node.color:BLACK; public boolean isred (rbnode<t> node) {//Determine the node's color return (node!= null) && (Node.color = = RED)? true:f
	Alse;
	public boolean Isblack (rbnode<t> node) {return!isred (node);
	The public void setred (rbnode<t> node) {//sets the color of the node (nodes!= null) Node.color = RED;
		public void Setblack (rbnode<t> node) {if (node!= null) {Node.color = black;
	} public void SetColor (rbnode<t> node, Boolean color) {if (node!= null) Node.color = color;
	/***************** the *********************/public void preorder () {preorder (root) of the red-black tree before the sequence is traversed; } private void Preorder (rbnode<t> tree) {if (tree!= null) {System.out.print (Tree.key + & 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.