【高次データ構造】 赤黒ツリー(C++で実装)

⭐ブログのホームページ: ️CS セミのホームページ
⭐フォロー歓迎: いいね、お気に入り、メッセージを残す
⭐ コラム シリーズ: 高度な C++
⭐ コード リポジトリ: 高度な C++
ホーム 人々が更新するのは簡単ではありません。あなたの「いいね!」と関心は私にとって非常に重要です。友達、いいね、フォローしてください。あなたのサポートが私の創作の最大の動機です。友達は質問するためにプライベートメッセージを送信することを歓迎します。家族メンバーの皆さん、忘れないでくださいいいね&集めて+フォロー! ! !


1. 赤黒木の概念

赤黒ツリーは二分探索ツリーですが、ノードの色を表すためにストレージ ビットが各ノードに追加されます。赤または黒にしてください。赤黒ツリーは、ルートからリーフまでのパス上の各ノードの色を制限することで、他のパスの 2 倍の長さのパスがないことを保証し、バランスに近くなります。

ここに画像の説明を挿入します

2. 赤黒木の性質

赤黒木の 5 つの特性:

1. 各ノードは赤または黒です
2. ルート ノードは黒です
3. ノードが赤の場合、その 2 つのノードは子ノードは黒です
4. 各ノードについて、そのノードからそのすべての子孫リーフ ノードへの単純なパスには、同じ数の黒いノードが含まれます。ポイント
5 . 各リーフ ノードは黒です (ここでのリーフ ノードは空のノードを指します)

次の質問について考える必要があります。なぜ赤黒ツリーは上記の特性を満たし、最長パス内のノード数がノード数を超えないようにするのか最短経路上のノードは 2 倍ですか?

性質 3 によれば、赤いノードの後に​​は黒いノードがなければならないことがわかります。赤黒ツリーに赤いノードが連続することは不可能です。性質 4 によると、単純な計算上では次のことがわかります。各ノードの子孫ノードのパス どちらにも同じ数の黒いノードが含まれます。
N 個の黒いノードがあると仮定すると、最短パスは完全に黒いノードで構成されるパスであり、その長さは N です。

ここに画像の説明を挿入します

可能な最長のパスは、下に間隔をあけて配置された 1 つの黒と 1 つの赤であるため、全長は 2N になります。

ここに画像の説明を挿入します
したがって、赤黒木の根から葉までの可能な最長経路は、可能な最短経路の 2 倍を超えません。

3. 赤黒ツリーノードの定義

K-Vモデルの赤黒ツリーを定義します。後続の回転操作を容易にするために、赤黒ツリーのノードを 3 本のチェーン構造として定義し、ノードの色を表すためにノードの色を定義する列挙型も追加します。

// 定义结点颜色
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. 赤黒ツリーノードの挿入

を挿入する前に、まず素朴な疑問として、毎回何色のノードを挿入すればよいでしょうか?
答えは赤いノードです。なぜですか?
黒ノードを挿入し、このパスの下に他のパスよりも黒ノードが 1 つ多いことが判明した場合、プロパティ 4 に違反するため、黒ノードと赤ノードを調整する必要があります。調整するノードの数はすべてでも構いませんが、赤いノードを挿入すると、この道路の黒いノードは追加されず、他の道路の黒いノードも追加されません。したがって、バランスが取れています。ただし、あるケースでは、その親ノードが赤です。赤いノードを挿入すると、連続した赤いノードが生成され、プロパティ 3 に違反します。そのため、これも調整する必要があります。以下にまとめます。

1. 黒のノードを挿入します。間違いは、このパスに沿って黒のノードが必然的に増加することになるという点です。これはプロパティ 4 に違反するため、色を調整する必要があります。
2. 赤のノードを挿入します。間違いは、その親ノードが赤である可能性があるため調整する必要があるということですが、その親ノードが元々黒であれば、調整する必要はありません。

したがって、メリットとデメリットを比較検討した結果、赤いノードを挿入することにしました。

挿入プロセス:(3 つのステップ、AVL ツリー挿入の全体的な考え方とほぼ同じ)

1. 検索: 二分探索木の挿入方法に従って挿入する位置を検索します。
2. 挿入: ツリーに挿入するノードを挿入します。
3. 調整: 挿入したノードの親ノードが赤の場合、赤黒ツリーを調整する必要があります。

赤黒ツリーは調整する必要がありますか?調整方法は?

すべての赤黒ツリーを調整する必要はありません。調整する必要があるノードの親ノードが黒ノードである場合もあります。赤いノードを挿入しても、ツリーには影響しないため、調整する必要はありませんこの場合はそうするように調整しました。

したがって、調整が必要になるのは、挿入されたノードの親ノードが赤色のノードである場合のみです。赤色のノードが連続して存在するためです。これは、2 番目のプロパティがルート ノードであるため、その親ノードがルート ノードではないことも意味します (ルートノード).) は黒なので、挿入したノードの祖父ノードが存在する必要がありますが、このとき赤黒ツリーの調整では親ノード、つまり叔父ノードの兄弟ノードを判断する必要があるため、おじさんノードに基づいてさまざまな状況を判断できます。:

ケース 1: 挿入された叔父ノードが存在し、赤色になっている

連続する赤いノードがないようにするには、親ノードを黒にしますが、各パスの黒いノードが同じになるようにしたいため、叔父ノードも黒にする必要があります。また、赤いノードが連続して発生する問題も解決します。
ここに画像の説明を挿入します

ただし、この時点では調整は完了していません。祖父ノードが赤黒ツリー全体のルート ノードであるかどうかがわからないため、判断する必要があります。

祖父母ノードがルート ノードの場合、グループの親ノードを黒に変更するだけで済みます。つまり、各道路に黒のノードが 1 つ追加されますが、これは影響しません。

そして、おじいさんノードがルートノードではない場合は、上向きに判断し続け、おじいさんの親ノードの色を判断し、それらをもとにおじいさんノードを判断する必要があります。
ここに画像の説明を挿入します

このとき、cur が親ノードの左の子であっても右の子であっても調整は同じです。
ここに画像の説明を挿入します

ケース 2: 挿入された叔父ノードが存在し、黒色である

この状況を詳細に分析し、それを表す図を描くことができます。
ここに画像の説明を挿入します
ここに画像の説明を挿入します

これが挿入したものですが、間違いを見つけたかどうかはわかりませんが、各パスの下にある黒いノードは同じであると言いませんでしたか?グループの親ノード g の上の黒いノードの数が x で、叔父ノードの下の黒いノードの数が y であると仮定します。その後、cur を挿入する前に、左側と右側の黒いノードの数は次のようになります: x+ 1 (NIL の空のノードまでのパス) および x+y+2。明らかに、右側のサブツリーには多くの黒いノードがあり、赤黒ツリーの要件をまったく満たしていません。したがって、この挿入状況は明らかに新規挿入中の状況ではなく、状況 1 からの更新プロセス中に存在します。

注:
1. 黒のノードを数えるときは、空になるまで数える必要があります。これが NIL ノードです。葉ノードで終わるわけではありません。ただし、その下の空のノードまでカウントします。
2. この場合、単純に色を変更するだけではだめなので、AVL ツリーと同様に回転操作を行う必要があり、回転後に上向きに調整し続ける必要はありません。手術。

2 つの状況:

直線

単一回転 (左または右回転) のみが使用されます。ここでは状況をリストします (右単一回転を使用 – p は g の左の子、cur は p の左の子です): < a i =1> 左回りの場合、p は g の右の子であり、cur は p の右の子です。
ここに画像の説明を挿入します

ポリライン

cur、p、gの3つのノードがポリラインになる場合は、二重回転操作を行ってから色を変更する必要がありますが、ここでは左右の二重回転、つまり左に一回転してから色を変更する操作について説明します。右一回転 抽象的な絵を描いてみましょう:

ここに画像の説明を挿入します

p が g の右側の子で、cur が p の左側の子の場合、左右の二重回転が使用されます。

ケース 3: 挿入されたノードの叔父ノードが存在しない

分析してみましょう。このノード cur は新しく挿入されたノードですか?まず、それを解決するための絵を描いてみましょう。
ここに画像の説明を挿入します

直線

下の図では、右側の単一回転を使用しています。
ここに画像の説明を挿入します

p が g の右子で、cur も p の右子である場合、左単回転を使用して色変更処理を実行します。

ポリライン

cur、p、g の 3 つのノードがポリラインになるとき、二重回転が必要になります。以下では左右の二重回転を使用します。

ここに画像の説明を挿入します

p が g の右の子で、cur が p の左の子である場合、左右 2 回転となり、色が変更されます。

コード操作

コードを記述する前に、このペアの山かっこには素晴らしい機能があることを理解する必要があります。つまり、挿入が成功したかどうかを示すために、ポインタが前部に格納され、ブール値が後部に格納されます。ペア

// 插入
	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. 赤黒木かどうかを確認する

まず、バランスの取れた二分木であるかどうかを確認します。

	// 中序遍历
	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. 赤と黒の木の高さ

実はこれは非常に簡単で、赤黒木の高さは左右の部分木の中で最も高い木の高さとして計算されるので、左右の部分木の中で最も高い部分木を計算するだけで済みます。

	// 计算树的高度
	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. 赤黒の木を探す

は、バイナリ ツリーの検索とまったく同じです。
1. 探している値が現在のノードより小さい場合は、左側のサブツリーに移動します。< a i=2> 2. 探している値が現在のノードより大きい場合は、右のサブツリーに移動します。 3. 探している値が現在のノードと等しい場合、見つかりました< /span> 4. null が見つからない場合は、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. 赤黒木の削除

最初のステップ: まず、削除するノードを見つけます。

これは二分木の探索とほぼ同じで、削除するノードの左右の部分木が空でないことがわかった場合は、置換法を使って削除する必要があるため、最終的に削除する必要があるのはノードです。左右のサブツリーに少なくとも 1 つの空のノードがあるもの。

ステップ 2: 赤黒ツリーを調整する

AVL ツリーと同様に、まず赤黒ツリーの色に影響を与えるかどうかを判断する必要があります。赤黒ツリーの 4 つのプロパティが破壊されている場合は、赤黒ツリーを調整する必要があります。

今回削除されたノードが赤ノードの場合、この削除操作によって赤黒ツリーのプロパティは破壊されないため、赤黒ツリーを調整する必要はありません。ただし、今回削除されたノードが黒ノードの場合、黒ノードの削減により 4 番目のプロパティが必然的に破壊されるため、この削除操作により必然的に赤黒ツリーのプロパティが破壊されます。そのため、赤黒ツリーは次のことを行う必要があります。調整されます。

(1) 状況 1

削除されるノードには、左の子/右の子という 1 つの子だけがあります。つまり、削除されるノードには、空の子が 1 つだけあります。

ここに画像の説明を挿入します

(2) 状況2

削除するノードの左右の子ノードは両方とも空です。
親ノードの左側の子ノードを削除する場合を例に挙げます。これは次の 4 つの状況に分類できます。

1. 兄は赤い

ここに画像の説明を挿入します
削除するノードの兄弟が赤の場合、親を回転点として左一回転し、兄弟をボスとして色を調整すると、兄弟は黒、親は赤になります。 、次に cur 分析を実行します。これは次の 3 つの状況です。

2. 兄弟は黒人で、彼の左右の子供たちはすべて黒いノードまたは空です。

ここに画像の説明を挿入します

3. 兄弟は黒で、その左の子は赤のノード、右の子は黒のノードまたは空です。

ここに画像の説明を挿入します
まず、brother を回転点として右回転し、次に Brother の色を赤に、brotherleft の色を黒に変更し、状況に応じて状況 4 に変更します。

4. 兄弟は黒人で、その右の子は赤いノードです

ここに画像の説明を挿入します

4 番目の状況に入ると、調整を終了し、親を回転ポイントとして左回転を 1 回実行し、次に親の色を兄弟に割り当て、次に親の色を黒に変更し、最後に兄弟の色を黒に変更します。

(3) 右サブツリー

右のサブツリーと左のサブツリーは似ています。

(4) 状況の説明

合計 4 つの状況があります。最も重要なことは、赤黒ツリーを適切に調整できるように 4 番目の状況に到達する必要があることです。したがって、次の関係があります。

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

関係はわかっているので、4 は確実に撤回でき、3 は 4 に変換して撤回でき、1 は 2、3 に変換でき、4 も撤回できます。2 だけの状況が最も混乱します。たった今 状況2に入り、親の色は謎ですが、黒でも赤でも問題ありません!

(5) 削除操作

二分木探索の削除ルールに従い、このノードの左右の子を接続するだけです。

コード

	// 删除
	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. 赤黒ツリーと AVL ツリーの比較

赤黒ツリーと AVL ツリーはどちらも効率的なバランスの取れたバイナリ ツリーであり、追加、削除、変更、チェックの時間計算量は O( l o g 2 N log_2 N あ>log2 N)、赤黒ツリーは絶対的なバランスを追求しません。最長のパスが最短のパスの 2 倍を超えないことだけを保証する必要があります。相対的に言えば、挿入と回転のコストが . 倍であるため、追加と削除が頻繁に実行される構造では AVL ツリーよりもパフォーマンスが良く、赤黒ツリーは実装が比較的簡単であるため、実際のアプリケーションではより多くの赤黒ツリーが存在します。


おすすめ

転載: blog.csdn.net/m0_70088010/article/details/132814022
おすすめ