[High-order data structure] Red-black tree (implemented in C++)

⭐Blog homepage: ️CS semi homepage
⭐Welcome to follow: like, favorite and leave a message
⭐ Column series: Advanced C++
⭐Code repository: Advanced C++
Home It is not easy for people to update. Your likes and attention are very important to me. Friends, please like and follow me. Your support is the biggest motivation for my creation. Friends are welcome to send private messages to ask questions. Family members, don’t forgetLike and collect + follow! ! !


1. The concept of red-black trees

The red-black tree is a binary search tree, but a storage bit is added to each node to represent the node's Color, can be Red or Black. By restricting the coloring of each node on any path from the root to a leaf, the red-black tree ensures that no path is twice as long as any other path, and is therefore close to balance< /span>.

Insert image description here

2. Properties of red-black trees

Five properties of red-black trees:

1. Each node is either red or black
2. The root node is black
3. If a node is red, then Its two child nodes are black
4. For each node, the simple path from the node to all its descendant leaf nodes contains the same number of black nodes. Point
5. Each leaf node is black (the leaf node here refers to the empty node)

We need to think about a question:Why does a red-black tree satisfy the above properties and ensure that the number of nodes in its longest path will not exceed the number of nodes in the shortest path? double?

According to property three, it shows that there must be a black node after a red node. It is impossible to have continuous red nodes in a red-black tree. According to property four, it shows that on the simple path of the descendant nodes of each node Both contain the same number of black nodes.
If we assume that there are N black nodes, then the shortest path is a path composed entirely of black nodes, and its length is N.

Insert image description here

The longest possible path is one black and one red spaced downwards, so the total length is 2N.

Insert image description here
Therefore, the longest possible path from the root to the leaves of a red-black tree is no more than twice the shortest possible path.

3. Definition of red-black tree nodes

We define a red-black tree of K-V model. In order to facilitate subsequent rotation operations, we define the nodes of the red-black tree as a three-pronged chain structure. We also add an enumeration type that defines the color of the node to represent the color of the node.

// 定义结点颜色
enum Col
{
    
    
	RED,
	BLACK
};

// 定义红黑树结点
template<class K, class V>
struct RBTreeNode
{
    
    

	// 构造函数
	RBTreeNode(const pair<K, V> kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(RED)
		,_kv(kv)
	{
    
    }

	// 三叉链
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	// 存储颜色
	int _col;

	// 存储键值对
	pair<K, V> _kv;
};

4. Insertion of red-black tree nodes

Before inserting , let us first think about a simple question. What color node should be inserted each time?
The answer is the red node, why?
Because if we insert a black node and find that there is one more black node under this path than under other paths, it will violate property four, so It is necessary to adjust the black nodes and red nodes. The number of nodes to be adjusted may be all. However, if we insert a red node, the black nodes on this road will not be added, and the black nodes on other roads will not be added. There is no increase, so it is balanced. However, in one case, its parent node is red. Inserting a red node will result in continuous red nodes, which violates property three, so it also needs to be adjusted. We summarize below:

1. Insert black nodes: The mistake is that it will inevitably lead to an increase in black nodes along this path, which violates property four and the color must be adjusted.
2. Insert a red node: The mistake is that its parent node may be red and needs to be adjusted, but if its parent node is originally black, there is no need to adjust.

Therefore, after weighing the pros and cons, we decided to insert the red node.

Insertion process: (three steps, roughly the same as the overall idea of ​​AVL tree insertion)

1. Find: Find the position to be inserted according to the insertion method of the binary search tree.
2. Insertion: Insert the node to be inserted into the tree.
3. Adjustment: If the parent node of the inserted node is red, the red-black tree needs to be adjusted.

Does the red-black tree need to be adjusted? How to adjust it?

Not all red-black trees need to be adjusted. In one case, the parent node of the node that needs to be adjusted is a black node. When we insert a red node, it will not affect the tree, so there is no need to do it in this case. adjusted.

Therefore, adjustment is only needed if the parent node of the inserted node is a red node, because there will be continuous red nodes, which also means that its parent node is definitely not the root node, because the second property is the root node (root node). ) is black, so the grandfather node of the inserted node must exist. At this time, the adjustment of the red-black tree requires judging the sibling nodes of the parent node, that is, the uncle node, so we can judge different situations based on the uncle node. :

Case 1: The inserted uncle node exists and is red

In order to maintain that there are no consecutive red nodes, we can make the parent node black. Because we want to ensure that the black nodes of each path are the same, we need to make the uncle node black as well. It also solves the problem of continuous red nodes.
Insert image description here

But the adjustment is not complete at this time, because we are not sure whether the grandfather node is the root node of the entire red-black tree, so we need to make a judgment:

If the grandparent node is the root node, we only need to change the group parent node to black, that is, one more black node is added to each road, which does not affect it.

And if the grandfather node is not the root node, we need to continue to judge upward, judge the color of the grandfather's parent node, and then judge the uncle node based on these.
Insert image description here

At this time, regardless of whether cur is the left child or the right child of the parent node, the adjustment is the same.
Insert image description here

Case 2: The inserted uncle node exists and is black

We can conduct a detailed analysis of this situation and draw a diagram to represent it:
Insert image description here
Insert image description here

This is what we inserted, but I don’t know if you have found an error. Didn’t we say that the black nodes under each path are the same? We assume that the number of black nodes above the group parent node g is x, and the number of black nodes below the uncle node is y. Then before inserting cur, the number of black nodes on our left and right sides are: x+1 (path All the way to the NIL empty node) and x+y+2. Obviously, the right subtree has many black nodes, which does not meet the requirements of a red-black tree at all! Therefore, this insertion situation is definitely not the situation during the new insertion, but exists during the update process from situation one.

Note:
1. When we count the black node, we need to count it until it is empty, which is our NIL node. It does not end at the leaf node, but Count to the empty node below it.
2. When this happens, we cannot simply rely on changing the color, so we need to perform a rotation operation like the AVL tree, and there is no need to continue to adjust upwards after the rotation operation.

Two situations:

a straight line

Only single rotation (left or right rotation) is used. Here we list a situation (using right single rotation – p is the left child of g, cur is the left child of p):
Insert image description here
In the case of left-handed rotation, p is the right child of g, and cur is the right child of p.

Polyline

If the three nodes of cur, p, and g become a polyline, it is necessary to perform a double rotation operation and then change the color. Here we will explain the left and right double rotation, that is, first left single rotation and then right single rotation. Let's draw an abstract picture:

Insert image description here

When p is the right child of g and cur is the left child of p, right-left double rotation is used.

Case 3: The uncle node of the inserted node does not exist

Let's analyze it, is this node cur a newly inserted node? Let’s first draw a picture to solve it:
Insert image description here

a straight line

The picture below uses right-hand single rotation:
Insert image description here

When p is the right child of g and cur is also the right child of p, we use left single rotation and then perform color change processing.

Polyline

When the three nodes cur, p, and g become a polyline, a double rotation is required. We use left and right double rotation below:

Insert image description here

If p is the right child of g and cur is the left child of p, it is a right-left double rotation, and then the color is changed.

Code operations

Before we write the code, we need to understand that the angle brackets in the pair have a wonderful function, that is, the pointer is stored in the front and the bool value is stored in the back to show whether the insertion is successful. We can take a brief look at: pair<Node*, true> and <Node*, false>. The former represents the insertion pointer, and the latter variable indicates whether the insertion is successful.

// 插入
	pair<Node*, bool> Insert(const pair<K, V>& kv)
	{
    
    
		// 一棵空树
		if (_root == nullptr)
		{
    
    
			// 创建新结点 + 颜色初始化为黑色
			_root = new Node(kv);
			_root->_col = BLACK; // 根节点得是黑的
			return make_pair(_root, true);
		}

		// 先找到 -- 利用二叉搜索树的方法进行查找
		Node* cur = _root;
		Node* parent = nullptr;
		// 左小右大
		while (cur)
		{
    
    
			// 当前结点值大于待插入结点值,往左子树走
			if (cur->_kv.first > kv.first)
			{
    
    
				parent = cur;
				cur = cur->_left;
			}
			// 当前结点值小于待插入结点值,往右子树走
			else if (cur->_kv.first < kv.first)
			{
    
    
				parent = cur;
				cur = cur->_right;
			}
			// 待插入结点的值和当前结点的值相等,插入失败
			else
			{
    
    
				return make_pair(cur, false);
			}
		}

		// 将当前结点的值插入进去
		cur = new Node(kv); // new一个新的结点
		cur->_col = RED;
		Node* newnode = cur; // 记录新插入的结点
		// 新插入的节点值小于父节点的节点值,插入到parent的左边
		if (kv.first < parent->_kv.first)
		{
    
    
			parent->_left = cur;
			cur->_parent = parent;
		}
		// 新插入的节点值小于父节点的节点值,插入到parent的左边
		else
		{
    
    
			parent->_right = cur;
			cur->_parent = parent;
		}

		// 新插入结点的父节点是红色的,需要做出调整
		while (parent && parent->_col == RED)
		{
    
    
			Node* grandfather = parent->_parent; // parent是红色,则其父结点一定存在
			// 以grandparent左右孩子为分界线,分成if和else
			if (parent == grandfather->_left) // 左孩子
			{
    
    
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)// 情况一:uncle存在且为红色
				{
    
    
					// 颜色调整
					grandfather->_col = RED;
					uncle->_col = parent->_col = BLACK;
					
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else// 情况二:uncle存在且为黑色 / 情况三:uncle不存在
				{
    
    
					// 用左右孩子分为两半,一半是if用来表示在左孩子,一半是else用来表示在右孩子
					if (cur == parent->_left)
					{
    
    
						//   g
						//  p
						// c
						// 右单旋
						RoateR(grandfather);

						// 颜色调整
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
    
    
						//    g
						//  p
						//      c
						// 左右双旋
						RoateLR(grandfather);

						// 颜色调整
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else // 右孩子
			{
    
    
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)// 情况一:uncle存在且为红色
				{
    
    
					// 颜色调整
					grandfather->_col = RED;
					uncle->_col = parent->_col = BLACK;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else// 情况二:uncle存在且为黑色 / 情况三:uncle不存在
				{
    
    
					// 用左右孩子分为两半,一半是if用来表示在左孩子,一半是else用来表示在右孩子
					if (cur == parent->_right)
					{
    
    
						//   g
						//     p
						//      c
						// 左单旋
						RoateL(grandfather);

						// 颜色调整
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
    
    
						//    g
						//      p
						//    c
						// 右左双旋
						RoateRL(grandfather);

						// 颜色调整
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return make_pair(newnode, true);
	}

	// 左单旋
	void RoateL(Node* parent)
	{
    
    
		// 三叉链
		Node* subr = parent->_right;
		Node* subrl = subr->_left;
		Node* ppnode = parent->_parent;

		// subrl与parent的关系
		parent->_right = subrl;
		if (subrl)
			subrl->_parent = parent;

		// subl和parent的关系
		subr->_left = parent;
		parent->_parent = subr;

		// ppnode和subr的关系
		if (ppnode == nullptr)
		{
    
    
			_root = subr;
			subr->_parent = nullptr;
		}
		else
		{
    
    
			if (ppnode->_left == parent)
			{
    
    
				ppnode->_left = subr;
			}
			else
			{
    
    
				ppnode->_right = subr;
			}
			subr->_parent = ppnode;
		}
	}

	// 右单旋
	void RoateR(Node* parent)
	{
    
    
		// 三叉链
		Node* subl = parent->_left;
		Node* sublr = subl->_right;
		Node* ppnode = parent->_parent;


		//sublr和parent之间的关系
		parent->_left = sublr;
		if (sublr)
			sublr->_parent = parent;

		//subl和parent的关系
		subl->_right = parent;
		parent->_parent = subl;


		//ppnode 和 subl的关系
		if (ppnode == nullptr)
		{
    
    
			_root = subl;
			subl->_parent = nullptr;
		}
		else
		{
    
    
			if (ppnode->_left == parent)
			{
    
    
				ppnode->_left = subl;
			}
			else
			{
    
    
				ppnode->_right = subl;
			}
			subl->_parent = ppnode;
		}
	}

	// 左右双旋
	void RoateLR(Node* parent)
	{
    
    
		RoateL(parent->_left);
		RoateR(parent);
	}

	// 右左双旋
	void RoateRL(Node* parent)
	{
    
    
		RoateR(parent->_right);
		RoateL(parent);
	}

5. Verify whether it is a red-black tree

First verify whether it is a balanced binary tree.

	// 中序遍历
	void InOrder()
	{
    
    
		return _InOrder(_root);
	}

	void _InOrder(Node* root)
	{
    
    
		if (root == nullptr)
			return;
		// 中序
		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}
	// 验证是否平衡
	// 先检查检查颜色
	bool CheckColour(Node* root, int blacknum, int blenchnum) // 基准值
	{
    
    
		if (root == nullptr)
		{
    
    
			// 每个路径黑色不相等
			if (blacknum != blenchnum)
			{
    
    
				return false;
			}
			return true;
		}
		// 黑色增加
		if (root->_col == BLACK)
		{
    
    
			++blacknum;
		}

		// 连续红色结点情况
		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
		{
    
    
			cout << root->_kv.first << "出现连续的红色结点" << endl;
			return false;
		}

		// 递归
		return CheckColour(root->_left, blacknum, blenchnum)
			&& CheckColour(root->_right, blacknum, blenchnum);
	}

	// 再检查是否平衡
	bool IsRBTree()
	{
    
    
		return _IsRBTree(_root);
	}

	bool _IsRBTree(Node* root)
	{
    
    
		if (root == nullptr)
		{
    
    
			return true;
		}

		if (root->_col == RED)
		{
    
    
			return false;
		}

		// 找最左路径作为黑色结点数目的参考值
		int blenchnum = 0;
		Node* cur = _root;
		while (cur)
		{
    
    
			if (cur->_col == BLACK)
				++blenchnum;
			cur = cur->_left;
		}

		return CheckColour(root, 0, blenchnum);
	}

6. Height of red and black trees

In fact, it is very simple. The height of the red-black tree is calculated as the height of the tallest tree among the left and right subtrees, so we only need to calculate the tallest subtree among the left and right subtrees.

	// 计算树的高度
	int Height()
	{
    
    
		return _Height(_root);
	}

	int _Height(Node* root)
	{
    
    
		if (root == nullptr)
		{
    
    
			return 0;
		}

		int leftcount = _Height(root->_left);
		int rightcount = _Height(root->_right);
		
		return leftcount > rightcount ? leftcount + 1 : rightcount + 1;
	}

7. Search for red-black trees

is exactly the same as searching a binary tree:
1. If the value you are looking for is smaller than the current node, go to the left subtree
2. The value you are looking for is greater than the current node, go to the right subtree
3. The value you are looking for is equal to the current node, found
4. If no null is found, return null

	// 红黑树的查找
	Node* Find(const pair<K, V>& kv)
	{
    
    
		Node* cur = _root;
		while (cur)
		{
    
    
			// 当前结点的值大于寻找的结点的值
			if (cur->_kv.first > kv.first)
			{
    
    
				cur = cur->_left;
			}
			else if(cur->_kv.first < kv.first)
			{
    
    
				cur = cur->_right;
			}
			else
			{
    
    
				// 找到了
				return cur;
			}
		}
		return nullptr;
	}

8. Deletion of red-black trees

The first step: first find the node to be deleted

It is roughly the same as searching a binary tree. If we find that the left and right subtrees of the node to be deleted are not empty, we need to use the substitution method to delete, so what we ultimately need to delete are the nodes that have at least one empty node in the left and right subtrees.

Step 2: Adjust the red-black tree

Also like the AVL tree, you first need to determine whether it affects the color of the red-black tree. If the four properties of the red-black tree are destroyed, you need to adjust the red-black tree.

If the node deleted this time is a red node, then this deletion operation will not destroy the properties of the red-black tree, so we do not need to adjust the red-black tree. However, if the node deleted this time is a black node, then this deletion operation will inevitably destroy the properties of the red-black tree, because the reduction of black nodes will inevitably destroy the fourth property, so the red-black tree needs to be adjusted. .

(1) Situation 1

The node to be deleted has only one child, left child/right child, that is, the node to be deleted has only one child that is empty.

Insert image description here

(2) Situation 2

The left and right child nodes of the node to be deleted are both empty.
Let’s take the node to be deleted as the left child of its parent node as an example. It can be divided into the following four situations:

1. Brother is red

Insert image description here
When the brother of the node to be deleted is red, first use the parent as the rotation point to perform a left single rotation, and use the brother as the boss, and then adjust the color. The brother becomes black, the parent becomes red, and we then do the cur Analysis, this is the following three situations.

2. Brother is black, and his left and right children are all black nodes or empty.

Insert image description here

3. Brother is black, and its left child is a red node, and its right child is a black node or empty.

Insert image description here
We first use brother as the rotation point to rotate right, then change the color of brother to red and the color of brotherleft to black, and then change it to situation four according to the situation.

4. Brother is black, and its right child is a red node

Insert image description here

When we enter the fourth situation, we end the adjustment, perform a single left rotation with parent as the rotation point, then assign the color of parent to brother, then change the color of parent to black, and finally change the brotherRight to black.

(3) Right subtree

The right subtree and the left subtree are similar.

(4) Description of the situation

We have a total of four situations. The most important thing is that we need to get to the fourth situation so that the red-black tree can be adjusted properly. Therefore, we have the following relationship:

1->2,3,4
2->1,2,3,4
3->4
4->结束

We know the relationship, so 4 can definitely be withdrawn, 3 can be transformed into 4 and then withdraw, 1 can be transformed into 2, 3, and 4 can also be withdrawn, and the situation with only 2 is the most confusing, because just now Entering the situation 2, the color of the parent is a mystery, whether it is black or red, there is no problem!

(5) Delete operation

According to the deletion rules of searching the binary tree, just connect the left/right children of this node.

code

	// 删除
	bool Erase(const K& key)
	{
    
    
		// 用于遍历二叉树找结点
		Node* parent = nullptr;
		Node* cur = _root;
		// 用于标记实际的删除结点及其父结点
		Node* delparentpos = nullptr;
		Node* delpos = nullptr;
		// 先找到
		while (cur)
		{
    
    
			// 所给key值小于当前节点的值 -- 往左树走
			if (key < cur->_kv.first)
			{
    
    
				parent = cur;
				cur = cur->_left;
			}
			// 所给key值大于当前结点的值 -- 往右树走
			else if (key > cur->_kv.first)
			{
    
    
				parent = cur;
				cur = cur->_right;
			}
			// 找到了
			else
			{
    
    
				// 左子树为空
				if (cur->_left == nullptr)
				{
    
    
					// 待删除结点是根节点
					if (cur == _root)
					{
    
    
						// 让根节点的右子树作为新的结点
						_root = _root->_right;
						if (_root)
							_root->_parent = nullptr;
						delete cur; // 删除原节点
						return true;
					}
					else // 不是根节点
					{
    
    
						delparentpos = parent; // 标记当前待删除结点的父节点
						delpos = cur; // 标记当前待删除的结点
					}
					break; // 删除结点有祖先的结点,需要更新平衡因子
				}
				// 右子树为空
				else if (cur->_right == nullptr)
				{
    
    
					// 待删除结点是根节点
					if (cur == _root)
					{
    
    
						// 让根节点的左子树作为新的结点
						_root = _root->_left;
						if (_root)
							_root->_parent = nullptr;
						delete cur; // 删除原节点
						return true;
					}
					else // 不是根节点
					{
    
    
						delparentpos = parent; // 标记当前待删除结点的父节点
						delpos = cur; // 标记当前待删除的结点
					}
					break; // 删除结点有祖先的结点,需要更新平衡因子
				}
				// 左右子树都不为空
				else
				{
    
    
					// 替换法
					// 寻找待删除结点的右子树中的最小值
					Node* minparent = cur;
					Node* minright = cur->_right;
					while (minright->_left)
					{
    
    
						minparent = minright; // 记录一下父节点
						minright = minright->_left; // 往左子树走
					}
					cur->_kv.first = minright->_kv.first;// 将待删除结点first替换为右子树的最小值
					cur->_kv.second = minparent->_kv.second;// 将待删除结点second替换为右子树的最小值
					// 记录一下要删除的父节点
					delparentpos = minparent;
					// 记录一下实际要删除的结点
					delpos = minright;
					break; // 祖先结点的平衡因子需要改变
				}
			}
		}
		// 没有被修改过,说明没找到当前要删除的结点
		if (delparentpos == nullptr)
			return false;

		// 记录当前要删除结点和当前要删除结点的父节点
		Node* del = delpos;
		Node* delP = delparentpos;

		// 调整红黑树
		if (delpos->_col == BLACK)
		{
    
    
			if (delpos->_left && delpos->_left->_col == RED) //待删除结点有一个红色的左孩子
			{
    
    
				//     delpos
				// _left
				delpos->_left->_col = BLACK; //将这个红色的左孩子变黑
			}
			else if (delpos->_right && delpos->_right->_col == RED) //待删除结点有一个红色的右孩子
			{
    
    
				// delpos
				//     _right
				delpos->_right->_col = BLACK; //将这个红色的右孩子变黑
			}
			else // 待删除结点的左右均为空
			{
    
    
				while (delpos != _root)
				{
    
    
					// 待删除的结点是其父节点的左孩子
					if (delpos == delparentpos->_left)
					{
    
    
						//      delparentpos
						//  delpos       brother
						Node* brother = delparentpos->_right; // 兄弟结点是其父结点的右孩子
						// 情况1:brother为红色
						if (brother->_col)
						{
    
    
							// 先左旋再调颜色
							RoateL(delparentpos);
							delparentpos->_col = RED;
							brother->_col = BLACK;
							// 继续向上调整
							brother = delparentpos->_right;
						}
						// 情况2:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
    
    
							brother->_col = RED;
							// 分情况
							if (delparentpos->_col == RED)
							{
    
    
								delparentpos->_col = BLACK;
								break;
							}
							// 向上调整
							delpos = delparentpos;
							delparentpos = delpos->_parent;
						}
						else
						{
    
    
							// 情况3:brother为黑色,且其右孩子是红色结点
							// 左旋
							RoateL(delparentpos);
							brother->_col = delparentpos->_col;
							delparentpos->_col = BLACK;
							brother->_right->_col = BLACK;
							break;

							// 情况4:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空
							if ((brother->_right == nullptr) || (brother->_right->_col == BLACK))
							{
    
    
								brother->_left->_col = BLACK;
								brother->_col = RED;
								RoateR(brother);

								brother = delparentpos->_right; 
							}
						}
					}
					// 待删除的结点是其父节点的右孩子
					else
					{
    
    
						Node* brother = delparentpos->_right; // 兄弟结点是其父结点的右孩子
						// 情况1:brother为红色
						if (brother->_col == RED)
						{
    
    
							RoateR(delparentpos);
							delparentpos->_col = RED;
							brother->_col = BLACK;
							// 继续向上调整
							brother = delparentpos->_left;
						}
						// 情况2:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
    
    
							brother->_col = RED;
							if (delparentpos->_col == RED)
							{
    
    
								delparentpos->_col = BLACK;
								break;
							}

							delpos = delparentpos;
							delparentpos = delpos->_parent;
						}
						else
						{
    
    
							// 情况3:brother为黑色,且其右孩子是红色结点
							RoateR(delparentpos);
							brother->_col = delparentpos->_col;
							delparentpos->_col = BLACK;
							brother->_left->_col = BLACK;
							break;

							// 情况4:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空
							if ((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							{
    
    
								brother->_right->_col = BLACK;
								brother->_col = RED;
								RoateL(brother);

								brother = delparentpos->_left;
							}
						}
					}
				}
			}
		}

		// 进行删除
		// 删除的结点的左子树是空树
		if (del->_left == nullptr)
		{
    
    
			if (del == delP->_left) //删除结点是其父结点的左孩子
			{
    
    
				delP->_left = del->_right;
				if (del->_right)
					del->_right->_parent = delP;
			}
			else //实际删除结点是其父结点的右孩子
			{
    
    
				delP->_right = del->_right;
				if (del->_right)
					del->_right->_parent = delP;
			}
		}
		else // 删除的结点的右子树是空树
			 // del->_right == nullptr 
		{
    
    
			if (del == delP->_left) //实际删除结点是其父结点的左孩子
			{
    
    
				delP->_left = del->_left;
				if (del->_left)
					del->_left->_parent = delP;
			}
			else //实际删除结点是其父结点的右孩子
			{
    
    
				delP->_right = del->_left;
				if (del->_left)
					del->_left->_parent = delP;
			}
		}
		delete del;
		return true;
	}

9. Comparison between red-black trees and AVL trees

Red-black trees and AVL trees are both efficient balanced binary trees, and the time complexity of adding, deleting, modifying and checking is O( l o g 2 N log_2 N log2N), the red-black tree does not pursue absolute balance. It only needs to ensure that the longest path does not exceed twice the shortest path. Relatively speaking, it reduces the cost of insertion and rotation. times, so the performance is better than AVL trees in structures where additions and deletions are often performed, and red-black trees are relatively simple to implement, so there are more red-black trees in practical applications.


Guess you like

Origin blog.csdn.net/m0_70088010/article/details/132814022