Pourquoi les arbres rouge-noir sont si populaires

Il existe de nombreux arbres de recherche binaires équilibrés, mais lorsque nous parlons d'arbres de recherche binaires équilibrés, les arbres rouge-noir sont souvent mentionnés, et leur "taux d'apparition" est encore plus élevé que celui des arbres de recherche binaires équilibrés.

L'arbre rouge-noir est un arbre de recherche binaire relativement équilibré, qui ne répond pas à la définition stricte d'un arbre de recherche binaire équilibré.

Table des matières

Insertion d'arbre rouge-noir

 Vérification de l'arbre rouge-noir


Propriétés des arbres rouge-noir

  • Chaque nœud est soit rouge soit noir
  • le noeud racine est noir 
  • Tout nœud adjacent au-dessus et au-dessous ne peut pas être rouge en même temps. Si un nœud est rouge, ses deux nœuds enfants sont noirs 
  •  Pour chaque nœud, tous les chemins du nœud à tous ses nœuds feuilles descendants contiennent le même nombre de nœuds noirs 
  • Chaque nœud feuille est un nœud noir vide, c'est-à-dire que le nœud feuille ne stocke pas de données.

Satisfaisant les propriétés ci-dessus, l'arbre rouge-noir peut garantir que le nombre de nœuds dans le chemin le plus long ne dépassera pas le double du nombre de nœuds dans le chemin le plus court.

Analyse des performances de l'arbre rouge-noir

En supposant que le nombre de nœuds noirs sur chaque chemin est N, le chemin le plus court est composé de tous les nœuds noirs et le nœud de chemin le plus long est un nœud noir et un nœud rouge alternativement, N<=longueur de chemin arbitraire<=2N.

L'arbre de recherche binaire équilibré est proposé pour résoudre le problème de dégradation des performances de l'arbre de recherche binaire dû aux mises à jour dynamiques. Par conséquent, "équilibré" peut être équivalent à aucune dégradation des performances, et "approximativement équilibré" peut être équivalent à une dégradation des performances moins grave. La complexité temporelle de nombreuses opérations sur un arbre de recherche binaire est proportionnelle à la hauteur de l'arbre. La hauteur d'un arbre binaire extrêmement équilibré (arbre binaire complet ou arbre binaire complet) est d'environ log n. Si vous voulez prouver que l'arbre rouge-noir est approximativement équilibré, il vous suffit de prouver que la hauteur de l'arbre rouge-noir arbre est approximativement log n (comme la même magnitude).

prouver:

 Le chemin le plus long de l'arbre rouge-noir ne dépassera pas 2log n, c'est-à-dire que la hauteur de l'arbre rouge-noir ne dépassera pas 2log n. Un arbre rouge-noir n'est que deux fois plus grand qu'un arbre AVL équilibré en hauteur, donc la perte de performances n'est pas importante. Comparé à l'arbre AVL, le coût de maintenance de l'arbre rouge-noir est inférieur, de sorte que les performances ne sont pas pires que l'arbre AVL.

Alors pourquoi les arbres rouge-noir sont-ils si populaires ? 

L'arbre AVL est un arbre binaire très équilibré, qui est très efficace pour trouver des données.Cependant, afin de maintenir cet équilibre élevé, l'arbre AVL doit payer plus. Afin de maintenir l'équilibre, la répartition des nœuds dans l'arborescence doit être ajustée à chaque fois que des données sont insérées ou supprimées, ce qui est complexe et prend du temps.
L'arbre rouge-noir n'est qu'approximativement équilibré, mais pas strictement défini. Par conséquent, le coût de maintien de l'équilibre est inférieur à celui de l'arbre AVL, mais la perte de performances n'est pas importante. Pour les applications d'ingénierie, nous préférons les arbres rouge-noir avec des coûts de maintenance et des performances relativement compromis. Plus important encore, la plupart des langages de programmation fournissent des classes qui encapsulent l'implémentation de l'arbre rouge-noir, que nous pouvons utiliser directement sans repartir de zéro, ce qui permet de gagner beaucoup de temps de développement. Un arbre rouge-noir est un arbre de recherche binaire approximativement équilibré. Il a été créé pour résoudre le problème de dégradation des performances causé par la mise à jour dynamique des données de l'arbre de recherche binaire. La hauteur de l'arbre rouge-noir est approximativement logn, et la complexité temporelle des opérations d'insertion, de suppression et de recherche est O(logn).
 

Définition de nœud d'arbre rouge-noir 

   //
    énumération de la couleur du nœud Couleur {ROUGE, NOIR} ;


    // Modèle de définition de nœud d'arbre rouge-noir
    <class T>
    struct RBTreeNode
    {         RBTreeNode(const T& data = T(), Color color = RED)             : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)             , _data(data ), _color(color)         {}         RBTreeNode<T>* _pLeft; // l'enfant gauche du nœud         RBTreeNode<T>* _pRight; // l'enfant droit du nœud         RBTreeNode<T>* _pParent; // le parent de le nœud         T_data ; // le champ de valeur du nœud         Color _color ; // la couleur du nœud     } ;









Lors de l'insertion d'un nœud, nous donnons la couleur par défaut du nœud rouge, car les nœuds noirs sur chaque chemin sont les mêmes, si le nœud est noir, cela affectera chaque chemin, et le nouveau nœud est rouge s'il ne le fait pas respecter les règles, il vous suffit d'ajuster ce chemin ou le chemin adjacent, et le coût de donner le nouveau nœud rouge est beaucoup plus petit que de donner le nouveau nœud noir.

Insertion d'arbre rouge-noir

L'arbre rouge-noir est basé sur l'arbre de recherche binaire avec ses contraintes d'équilibre, de sorte que l'insertion de l'arbre rouge-noir peut être divisée en deux étapes :

1. Insérer de nouveaux nœuds selon les règles de l'arbre de recherche binaire

2. Après avoir détecté l'insertion d'un nouveau nœud, si la nature de l'arbre rouge-noir est endommagée

Étant donné que la couleur par défaut d'un nouveau nœud est le rouge, si la couleur de son nœud parent est le noir, il ne viole aucune propriété de l'arbre rouge-noir et aucun ajustement n'est requis ; mais lorsque la couleur du nœud parent du Le nœud nouvellement inséré est rouge, il viole les propriétés de l'arbre rouge-noir, il ne peut pas y avoir de nœuds rouges connectés ensemble. À ce stade, il est nécessaire de discuter de l'arbre rouge-noir en fonction de la situation :

cur est le nœud courant, p est le nœud parent, g est le nœud grand-parent, u est le nœud oncle

Le nœud cur n'est pas nécessairement un nœud nouvellement ajouté, il peut s'agir d'un nœud pointé après ajustement.

Cas 1 : cur est rouge, p est rouge, g est noir, u existe et est rouge

Solution : changez p, u en noir, g en rouge, puis prenez g comme cur et continuez à ajuster vers le haut.

Cas 2 : cur est rouge, p est rouge, g est noir, u n'existe pas/u existe et est noir 

Il y a deux cas de u
: 1. Si le nœud u n'existe pas, alors cur doit être un nœud nouvellement inséré, car si cur n'est pas un nœud nouvellement inséré, alors cur et p doivent avoir un nœud dont la couleur est noire, ce qui ne satisfait pas la propriété : each Le nombre de nœuds noirs dans le chemin est le même.
2. Si le nœud u existe, il doit être noir. Ensuite, la couleur d'origine du nœud cur doit être noire. La raison pour laquelle il est rouge maintenant est que le sous-arbre cur change la couleur du nœud cur de Noir à rouge.

Solution : si p est l'enfant gauche de g, et cur est l'enfant gauche de p, effectuer une seule rotation à droite ; au contraire, si p est l'enfant droit de g, et cur est l'enfant droit de p, alors effectuer une seule rotation à gauche p et g changent de couleur -- p devient noir, g devient rouge 

Cas 3 : cur est rouge, p est rouge, g est noir, u n'existe pas/u existe et est noir

p est l'enfant gauche de g, cur est l'enfant droit de p, alors faites une simple rotation à gauche pour p ; au contraire, si p est l'enfant droit de g, cur est l'enfant gauche de p, alors faites une rotation à droite rotation unique pour p, puis convertie en cas 2

Code:

bool Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (data > cur->_data)
			{
				parent = cur;
				cur = cur->_right
			}
			else if (data < >> cur->_data)
			{
				parent = cur;
				cur = cur->_left
			}
			else 
			{
				return false;
			}
		}
		//插入节点
		cur = new Node(data);
		cur->_col(RED);
		if (parent->data < data)
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		//控制平衡
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = parent->_right;
				//1.uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					//变色+继续向上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else//2.uncle不存在/uncle存在且为黑
				{
					//        g
					//     p
					//  c

					//        g
					//     p
					//        c
					if (cur == parent->_left)
					{
						//右旋
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;

					}
					if (cur == parent->_right)
					{
						//先左旋再右旋
						RotateL(parent);
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;

					}
					break;
				}
			}
			else // parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					// 变色+继续向上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else // 2 + 3、uncle不存在/ 存在且为黑
				{
					//  g    
					//     p
					//        c

					//  g
					//     p
					//  c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}

 Vérification de l'arbre rouge-noir

La détection des arbres rouge-noir est divisée en deux étapes :

1. Vérifiez s'il satisfait l'arbre de recherche binaire (si le parcours dans l'ordre est une séquence ordonnée)

2. Vérifiez s'il satisfait aux propriétés de l'arbre rouge-noir

bool IsBalance()
	{
		if (_root && _root->_col == RED)
		{
			cout << "根节点不是黑色" << endl;
			return false;
		}
		
		// 最左路径黑色节点数量做基准值
		int banchmark = 0;
		Node* left = _root;
		while (left)
		{
			if (left->_col == BLACK)
				++banchmark;
			
			left = left->_left;
		}

		int blackNum = 0;
		return _IsBalance(_root, banchmark, blackNum);
	}

	bool _IsBalance(Node* root, int banchmark, int blackNum)
	{
		if (root == nullptr)
		{
			if (banchmark != blackNum)
			{
				cout << "存在路径黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "出现连续红色节点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			++blackNum;
		}

		return _IsBalance(root->_left, banchmark, blackNum)
			&& _IsBalance(root->_right, banchmark, blackNum);
	}

Je suppose que tu aimes

Origine blog.csdn.net/m0_55752775/article/details/129412112
conseillé
Classement