Explicación detallada de c ++ --- encapsulación de mapas y conjuntos

Prefacio

A través del estudio anterior, sabemos que los datos almacenados en el contenedor establecido son k, y los datos almacenados en el contenedor del mapa son k y v, pero la capa inferior de estos dos contenedores se implementa a través de árboles rojo-negro, por lo que de acuerdo con Nuestro pensamiento normal es que el árbol rojo-negro adapta estos dos contenedores respectivamente, es decir, implementa un iterador de versión kv para el mapa e implementa un iterador de versión k para el conjunto, pero si se implementa de esta manera, definitivamente será Es muy problemático, hará muchas de las mismas cosas y C ++ tiene plantillas, entonces, ¿podemos usar plantillas para hacer que los dos contenedores contengan diferentes cantidades de datos, pero se pueden encapsular mediante una plantilla de clase de árbol rojo-negro en un mapa o conjunto? La respuesta es sí, la biblioteca estándar que utilizamos utiliza un árbol rojo-negro para encapsular el mapa y el conjunto, luego implementaremos la encapsulación del mapa y el conjunto paso a paso.

Código básico del árbol rojo-negro.

enum colour
{
    
    
	RED,
	BLACK,
};
template<class K, class V>
struct RBTreeNode
{
    
    
	RBTreeNode(const pair<K, V>& _kv)
		:parent(nullptr)
		, right(nullptr)
		, left(nullptr)
		, kv(_kv)
		, _col(RED)
	{
    
    }
	RBTreeNode<K, V>* parent;
	RBTreeNode<K, V>* right;
	RBTreeNode<K, V>* left;
	pair<K, V> kv;
	colour _col;
};
template<class K, class V>
class RBTree
{
    
    
	typedef RBTreeNode<K, V> Node;
public:
	RBTree()
		:root(nullptr)
	{
    
    }
	bool insert(const pair<K, V>& _kv)
	{
    
    
		if (root == nullptr)
		{
    
    
			root = new Node(_kv);
			root->_col = BLACK;
			return true;
		}
		Node* _cur = root;
		Node* _parent = nullptr;
		while (_cur != nullptr)
		{
    
    
			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 false;
			}
		}
		_cur = new Node(_kv);
		if (_kv.first > _parent->kv.first)
			//如果插入的数据比parent的数据大
		{
    
    
			_parent->right = _cur;
			_cur->parent = _parent;
		}
		else
			//如果插入的数据比parent的数据笑
		{
    
    
			_parent->left = _cur;
			_cur->parent = _parent;
		}
		while (_parent != nullptr && _parent->_col == RED)
		{
    
    
			Node* _grandparent = _parent->parent;
			if (_grandparent->left == _parent)
				//祖先节点的左边要调整
			{
    
    
				Node* uncle = _grandparent->right;//parent在左边所以uncle在右边
				//情况一
				if (uncle != nullptr && uncle->_col == RED)
				{
    
    
					uncle->_col = _parent->_col = BLACK;
					if (_grandparent->parent != nullptr)
					{
    
    
						_grandparent->_col = RED;
					}
					_cur = _grandparent;
					_parent = _grandparent->parent;
				}
				else
				{
    
    
					//情况二
					if (_cur == _parent->left)
					{
    
    
						RototalR(_grandparent);
						_parent->_col = BLACK;
						_grandparent->_col = RED;
					}
					else//情况三
					{
    
    
						RototalL(_parent);
						RototalR(_grandparent);
						_cur->_col = BLACK;
						_grandparent->_col = RED;
					}
					break;
				}

			}
			else
				//祖先节点的右边要调整
			{
    
    
				Node* uncle = _grandparent->left;//parent在右边所以uncle在左边
				//情况一
				if (uncle != nullptr && uncle->_col == RED)
				{
    
    
					uncle->_col = _parent->_col = BLACK;
					if (_grandparent->parent != nullptr)
					{
    
    
						_grandparent->_col = RED;
					}
					_cur = _grandparent;
					_parent = _grandparent->parent;
				}
				else
				{
    
    
					//情况二
					if (_cur == _parent->right)
					{
    
    
						RototalL(_grandparent);
						_parent->_col = BLACK;
						_grandparent->_col = RED;
					}
					else//情况三
					{
    
    
						RototalR(_parent);
						RototalL(_grandparent);
						_cur->_col = BLACK;
						_grandparent->_col = RED;
					}
					break;
				}
			}
		}
		return true;

	}
	void inorder()
	{
    
    
		_inorder(root);
	}
	bool find(const K& val)
	{
    
    
		Node* cur = root;
		while (cur)
		{
    
    
			if (cur->kv.first == val)
			{
    
    
				return true;
			}
			else if (cur->kv.first > val)
			{
    
    
				cur = cur->left;
			}
			else
			{
    
    
				cur = cur->right;
			}
		}
		return false;
	}
	bool check(Node* root, int BlackNums, int ref)
	{
    
    
		if (root == nullptr)
		{
    
    
			if (ref != BlackNums)
			{
    
    
				cout << "违反规则" << endl;
				return false;
			}
			return true;
		}
		if (root->_col == RED && root->parent->_col == RED)
		{
    
    
			cout << "出现了连续红色节点" << endl;
		}
		if (root->_col == BLACK)
		{
    
    
			BlackNums++;
		}
		return check(root->left, BlackNums, ref) && check(root->right, BlackNums, ref);
	}
	bool IsBalance()
	{
    
    
		if (root == nullptr)
		{
    
    
			return true;
		}
		if (root->_col != BLACK)
		{
    
    
			return false;
		}
		int ref = 0;
		Node* _left = root;
		while (_left)
		{
    
    
			if (_left->_col == BLACK)
			{
    
    
				ref++;
			}
			_left = _left->left;
		}
		return check(root, 0, ref);

	}
private:
	Node* root;
	void _inorder(const Node* root)
	{
    
    
		if (root == nullptr)
		{
    
    
			return;
		}
		_inorder(root->left);
		cout << root->kv.first << " " << root->kv.second << endl;
		_inorder(root->right);
	}
	void RototalL(Node* _parent)
	{
    
    
		Node* subR = _parent->right;//右孩子的节点
		Node* subRL = subR->left;//右孩子的左节点
		Node* ppNode = _parent->parent;//祖父节点
		//把subRL放到_parent的右
		_parent->right = subRL;
		if (subRL)
		{
    
    
			//如果subRL不为空则修改父节点的指向
			subRL->parent = _parent;
		}
		//把_parent放到subR的左
		subR->left = _parent;
		//修改_parent的parent的指向
		_parent->parent = subR;
		if (ppNode)//如果祖父不为空,则要改变祖父的指向
		{
    
    
			if (ppNode->right == _parent)
			{
    
    
				ppNode->right = subR;
				subR->parent = ppNode;
			}
			else//如果_parent是祖父的左
			{
    
    
				ppNode->left = subR;
				subR->parent = ppNode;
			}
		}
		else//祖父为空节点说明当前调整的是根节点
		{
    
    
			root = subR;
			subR->parent = nullptr;
		}
	}
	//右旋转
	void RototalR(Node* _parent)
	{
    
    
		Node* subL = _parent->left;
		Node* subLR = subL->right;
		Node* ppNode = _parent->parent;
		_parent->left = subLR;
		if (subLR)
		{
    
    
			subLR->parent = _parent;
		}
		subL->right = _parent;
		_parent->parent = subL;
		if (ppNode != nullptr)
		{
    
    
			if (ppNode->right == _parent)
			{
    
    
				ppNode->right = subL;
				subL->parent = ppNode;
			}
			else
			{
    
    
				ppNode->left = subL;
				subL->parent = ppNode;
			}
		}
		else
		{
    
    
			root = subL;
			subL->parent = nullptr;
		}
	}
};

Si no conoce el principio de los árboles rojo-negro, o si no se pueden implementar los árboles rojo-negro, puede leer mi último artículo.

Encapsulación de mapa y conjunto.

Sabemos que el contenedor de conjuntos contiene un elemento k y el contenedor de mapas contiene dos elementos k y v. Según el estudio anterior, sabemos que el mapa son dos elementos cargados usando pares. Lógicamente hablando, las tiendas de mapas son un par de elementos, por lo que ¿Puede la plantilla del árbol rojo-negro ser un elemento? Entonces aquí lo analizamos paso a paso: si la plantilla del árbol rojo-negro es un elemento, la encapsulación del mapa y el conjunto debería verse así:

template<class K>
class set
{
    
    
public:

private:
	RBTree<K> tree;
};

template<class K,class V>
class map
{
    
    
public:

private:
	RBTree<pair<const K,V>> tree;

};

Si lo que paso es una K, entonces lo que el árbol rojo-negro instancia se establece. Si lo que paso es un par, entonces lo que el árbol rojo-negro instancia es un mapa. Parece razonable, pero en la biblioteca estándar parece Eso es "No es cómo funciona. En la biblioteca estándar, map usa dos parámetros k y pair para crear instancias, y set también usa dos parámetros para crear instancias, pero ambos parámetros son k. Las capas subyacentes de map y set se basan en rojo. El árbol negro es Se utiliza para encapsulación. Si el segundo parámetro es k, entonces se crea una instancia del conjunto. Si el segundo parámetro es par, se crea una instancia del mapa. Por ejemplo, el siguiente código:

template<class K>
class set
{
    
    
public:

private:
	RBTree<K,K> tree;
};

#include"RBTree.h"
template<class K,class V>
class map
{
    
    
public:

private:
	RBTree<K,pair<const K,V>> tree;

};

Entonces hay un problema aquí: dado que el segundo parámetro de plantilla puede determinar quién está en el mapa y quién está configurado, ¿por qué necesitamos pasar el primer parámetro de plantilla? Para aclarar este problema, primero echemos un vistazo a lo que sucederá si solo hay un parámetro. El primero es la función de inserción. Los parámetros de esta función son los siguientes:

itertaor insert(const V& date)

Si el contenedor actual es un conjunto, el tipo de V aquí es K. Si el contenedor actual es un mapa, el tipo de V aquí es par. Y normalmente cuando usamos la función insertar para pasar parámetros, también es correspondiente. Los datos se insertan en el conjunto. Es K. Al insertar datos en el mapa, se pasa el par. Si la plantilla del árbol rojo-negro tiene solo un parámetro, se puede tratar aquí. ¿Qué pasa si es el hallazgo? ¿función? Ya sea un contenedor de mapa o un contenedor de búsqueda, lo que pasamos cuando usamos la función de búsqueda son datos de tipo K. No se puede decir que si el contenedor actual es un mapa, pasaremos el par para buscar, ¿verdad? Entonces, si el árbol rojo-negro tiene solo un parámetro, ¿cómo declarar la función de búsqueda en el contenedor del mapa? Por lo tanto, para encapsular correctamente el mapa y el conjunto, el árbol rojo-negro aquí debe tener dos parámetros de plantilla para representar el tipo de datos. El árbol rojo-negro actual debe encapsular tanto el mapa como el conjunto, por lo que también debemos hacer la estructura. describiendo el nodo. Sin modificaciones, la forma anterior de la estructura era la siguiente:

template<class K, class V>
struct RBTreeNode
{
    
    
	RBTreeNode(const pair<K, V>& _kv)
		:parent(nullptr)
		, right(nullptr)
		, left(nullptr)
		, kv(_kv)
		, _col(RED)
	{
    
    }
	RBTreeNode<K, V>* parent;
	RBTreeNode<K, V>* right;
	RBTreeNode<K, V>* left;
	pair<K, V> kv;
	colour _col;
};

Dijimos anteriormente que el segundo parámetro del árbol rojo-negro puede determinar si el árbol actual encapsula un mapa o un conjunto, por lo que aquí está la misma razón: la estructura solo necesita un parámetro para determinar si la instanciación actual es un nodo de mapa o un nodo establecido, por lo que cambiamos el número de parámetros de la plantilla a un tipo utilizado para expresar los datos almacenados, y luego modificamos la variable de par para que sea una variable creada por el tipo de plantilla, por ejemplo, el siguiente código:

template< class T>
struct RBTreeNode
{
    
    
	RBTreeNode(const T& _data)
		:parent(nullptr)
		, right(nullptr)
		, left(nullptr)
		, data(_data)
		, _col(RED)
	{
    
    }
	RBTreeNode<T>* parent;
	RBTreeNode<T>* right;
	RBTreeNode<T>* left;
	T data;
	colour _col;
};

Pero aquí surge un nuevo problema: dentro de la función de inserción y la función de búsqueda, se comparan los parámetros pasados. Para set, esta comparación es normal. Puedes comparar directamente el valor k. ¿Qué pasa con el mapa? ¿Puede comparar directamente los pares internos? Entonces hay dos preguntas aquí: en primer lugar, ¿pueden los pares utilizar el operador ">" "<" para comparar y, en segundo lugar, las reglas de esta comparación cumplen con nuestras expectativas? La respuesta a la primera pregunta es que puede usar el operador para comparar. Pair proporciona una sobrecarga del operador, como en la siguiente imagen: Dado que se puede
Insertar descripción de la imagen aquí
realizar la comparación, echemos un vistazo a la lógica de sobrecarga de la segunda pregunta. ¿Cumple nuestras expectativas? Para el mapa, esperamos que la regla de comparación para sus datos internos sea que cuanto mayor sea el primer elemento del par, mayor será el valor del par. No tiene nada que ver con el segundo elemento. ¿Es así como se sobrecarga la biblioteca? ¿implementado? Echemos un vistazo a la siguiente imagen:
Insertar descripción de la imagen aquí
Si miras con atención, encontrarás que la implementación aquí es diferente de lo que imaginamos. También agrega el segundo elemento a la comparación de tamaño del par, por lo que si usas el par directamente en el map Definitivamente habrá problemas al comparar. Solo queremos usar el primer elemento del par para comparar, es decir, usar K para comparar. Entonces, ¡cómo resolver este problema aquí! La respuesta es agregar un funtor a la plantilla de árbol rojo-negro. La función de este functor es obtener específicamente la K de los datos internos del contenedor. Primero, este funtor requiere un parámetro. Para establecer, el tipo de este parámetro es K. Para el mapa, el tipo de este parámetro es par, por ejemplo, el siguiente código:

//set.h
	struct SetofKey
	{
    
    
		const K& operator()(const K& key)
		{
    
    
		}
	};
//map.h
	struct MapofKey
	{
    
    
		const K& operator()(const pair<const K, V>& kv)
		{
    
    
			
		}
	};

Para los datos pasados ​​por el functor de conjunto son la clave, podemos devolverla directamente. Para los datos pasados ​​por el funtor de mapa son un par, simplemente devolvemos el primer elemento del par. Entonces el código aquí es el siguiente:

//set.h
	struct SetofKey
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
//map.h
	struct MapofKey
	{
    
    
		const K& operator()(const pair<const K, V>& kv)
		{
    
    
			return kv.first;
		}
	};

Ahora que se ha agregado el funtor, ¿es necesario modificar el árbol rojo-negro al mostrar la creación de instancias? Entonces el código aquí es el siguiente:

//set.h
template<class K>
class set
{
    
    
public:

private:
	struct SetofKey
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};

	RBTree<K,K, SetofKey> tree;

};
//map.h
template<class K,class V>
class map
{
    
    
public:

private:
	struct MapofKey
	{
    
    
		const K& operator()(const pair<const K, V>& kv)
		{
    
    
			return kv.first;
		}
	};

	RBTree<K,pair<const K,V>,MapofKey> tree;
};

Luego de agregar el functor, también se debe modificar la lógica de comparación del árbol rojo-negro, la lógica de comparación anterior era la siguiente:

template<class K, class V>
class RBTree
{
    
    
	typedef RBTreeNode<K,V> Node;
	bool insert(const pair<K, V>& _kv)
	{
    
    
		//.....
		Node* _cur = root;
		Node* _parent = nullptr;
		while (_cur != nullptr)
		{
    
    
			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 false;
			}
		}
		//.....
	}
}

Con el funtor, no podemos confiar en nosotros mismos para obtener K en los datos, sino que utilizamos el objeto funtor creado por el funtor para obtener el valor clave de los datos en el nodo. No olvide modificar el tipo de parámetro y el nodo. de la función de inserción aquí, el número de parámetros instanciados y el número de parámetros de la plantilla de árbol rojo-negro, entonces el código modificado aquí es así:

template<class K, class T,class KeyofT>
class RBTree
{
    
    
public:
typedef RBTreeNode<T> Node;
	bool insert(const T& _data)
	{
    
    
		//....
		KeyofT kot;
		Node* _cur = root;
		Node* _parent = nullptr;
		while (_cur != nullptr)
		{
    
    
			if (kot(_cur->data) > kot(_data))
			{
    
    
				_parent = _cur;
				_cur = _cur->left;
			}
			else if (kot(_cur->data) < kot(_data))
			{
    
    
				_parent = _cur;
				_cur = _cur->right;
			}
			else
			{
    
    
				return false;
			}
		}
		//....
	}
}

Por supuesto, los lugares que deben modificarse aquí son muchos más que los anteriores. Siempre que se utilice la primera comparación usando pares, se deben realizar modificaciones. Debido al problema de espacio, no puedo mostrarlos uno por uno aquí. Luego, después de la modificación, las funciones de inserción y búsqueda de map y set pueden llamar a las funciones de búsqueda e inserción del árbol rojo-negro para implementar, por ejemplo, el siguiente código:

//map.h
template<class K, class V>
class map
{
    
    
public:
	bool insert(const pair<K,V>& data)
	{
    
    
		return tree.insert(data);
	}
	bool find(const K& key)
	{
    
    
		return tree.find(key);
	}
private:
	struct MapofKey
	{
    
    
		const K& operator()(const pair<const K, V>& kv)
		{
    
    
			return kv.first;
		}
	};
	RBTree<K, pair<const K, V>, MapofKey> tree;
};
//set.h
template<class K>
class set
{
    
    
public:
	bool insert(const K& key)
	{
    
    
		return tree.insert(key);
	}
	bool find(const K& key)
	{
    
    
		return tree.find(key);
	}
private:
	struct SetofKey
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
	RBTree<K, K, SetofKey> tree;

Iterador de árbol rojo-negro

Primero echemos un vistazo a cómo se implementan los iteradores en la biblioteca:
Insertar descripción de la imagen aquí

Se proporciona un nodo de encabezado en la biblioteca para que apunte al nodo raíz del árbol, y el puntero izquierdo del encabezado apunta al nodo más a la izquierda, por lo que la función de inicio del iterador apunta a la izquierda del encabezado y el puntero derecho del encabezado apunta al nodo más a la derecha, por lo que la iteración La función final del dispositivo apunta al encabezado, por lo que esta es la forma en que se implementa el iterador en la biblioteca, entonces no agregamos el nodo de encabezado por conveniencia, la función de inicio aún Devuelve el nodo más a la izquierda y la función final devuelve directamente un puntero nulo. Entonces aquí podemos implementar estas dos funciones. Según la experiencia de la lista anterior, sabemos que el iterador aquí no se puede implementar dentro de la clase de árbol rojo-negro. pero se debe crear una clase por separado, porque actualmente queremos implementar un iterador común, por lo que esta plantilla de clase solo tiene un parámetro para representar el tipo de datos que desea el iterador actual, porque necesitamos realizar algunas operaciones en esta clase, como como encontrar el nodo más a la izquierda o el siguiente nodo, etc. Espere, entonces tenemos que crear una variable de este tipo de nodo en esta clase para realizar algunas operaciones. Debe haber una sobrecarga de operadores de ++ y – en el iterador. El tipo de retorno de estas funciones es el iterador en sí, por lo que para la conveniencia de lo siguiente Al escribir, cambiaremos el nombre de los tipos utilizados comúnmente, debido a que hay variables miembro en esta clase, por lo que tenemos que implementar el constructor de esta clase, luego el código aquí es como sigue:

template<class V>
struct _RBTreeIterator
{
    
    
	typedef RBTreeNode<V> Node;
	typedef _RBTreeIterator<V> self;
	Node* _node;
	_RBTreeIterator( Node* node)
		:_node(node)
	{
    
    }
};

Luego podemos implementar varias funciones simples de sobrecarga de operadores. La primera es la sobrecarga de desreferencia. Esta función devuelve directamente la referencia a los datos en el nodo actual. La segunda es -> sobrecarga de operadores. La función de esta función es devolver el dirección de los datos internos del nodo actual. La tercera es la sobrecarga del operador! =. Estas tres funciones son muy simples. No las explicaremos. Solo puede mirar el código:

template<class V>
struct _RBTreeIterator
{
    
    
	typedef RBTreeNode<V> Node;
	typedef _RBTreeIterator<V> self;
	Node* _node;
	_RBTreeIterator( Node* node)
		:_node(node)
	{
    
    }
	V& operator*()
	{
    
    
		return _node->data;
	}
	V* operator->()
	{
    
    
		return &_node->data;
	}
	bool operator !=(const self& s)
	{
    
    
		return _node != s._node;
	}
};

Decimos que los resultados de la búsqueda de un árbol binario usando el recorrido en orden están ordenados, y el orden del recorrido en orden es visitar primero el subárbol izquierdo, luego el nodo raíz y finalmente el subárbol derecho.Usar ++ significa deje que el nodo actual dé un paso atrás, el nodo que permanece actualmente debe ser el nodo raíz de un determinado subárbol. Si regresa en este momento, debe llegar al nodo más a la izquierda del subárbol derecho, pero hay una situación aquí. ¿Qué pasa si el nodo correcto no existe? Bien, si el nodo derecho no existe, significa que nuestro subárbol actual ha sido visitado y tenemos que visitar el subárbol superior, pero también vale la pena discutir si el subárbol aquí, si el subárbol actual es el subárbol izquierdo, entonces estamos ¿No es necesario visitar la raíz de la capa superior? Si el subárbol actual es el subárbol derecho, ¿debe continuar ajustándose hacia arriba hasta encontrar un nodo actual que esté a la izquierda del nodo raíz o del nodo principal? ¿Está vacío? Primero, juzgúeme si el subárbol derecho está vacío, si no, vaya al subárbol más a la izquierda del subárbol derecho, entonces el código aquí es el siguiente:

self& operator++()
{
    
    
	if (_node->right)
	{
    
    
		Node* mine = _node->right;
		while (mine->left)
		{
    
    
			mine = mine->left;
		}
		_node = mine;
	}
	else
	{
    
    
	}
}

Si el subárbol derecho está vacío, significa que el subárbol actual ha sido atravesado. En este momento, debemos subir. Si el nodo actual está a la izquierda del nodo principal, nos detendremos. Si el nodo actual está a a la derecha del nodo padre, tenemos que continuar con el ajuste, por lo que aquí tenemos que crear dos punteros de Nodo*, uno llamado parent para apuntar al nodo padre y otro llamado cur para apuntar al nodo actual. , juzgaremos la relación posicional entre cur y parent y si parent está vacío. El cuerpo del bucle es asignar el valor de parent a cur, luego dejar que parent apunte al padre de parent y finalmente asignar el valor de parent a _node , entonces el código aquí es el siguiente:

	self& operator++()
	{
    
    
		if (_node->right)
		{
    
    
			Node* mine = _node->right;
			while (mine->left)
			{
    
    
				mine = mine->left;
			}
			_node = mine;
		}
		else
		{
    
    
			Node* parent = _node->parent;
			Node* cur = _node;
			while (parent != nullptr && parent->right == cur)
			{
    
    
				cur = parent;
				parent = parent->parent;
			}
			_node = parent;
		}
		return *this;
	}

Iterador de árbol rojo-negro - -

El orden del recorrido en orden es visitar primero el subárbol izquierdo, luego visitar el nodo raíz y finalmente visitar el subárbol derecho. - - Va hacia atrás, por lo que el orden de los nodos visitados aquí es visitar primero el subárbol derecho y luego visite el nodo raíz y finalmente visite el subárbol izquierdo Iterar La ubicación del iterador debe ser el nodo raíz de un determinado subárbol. Cuando el iterador está ubicado en esta ubicación, significa que se ha visitado el subárbol derecho del nodo. A continuación, se debe acceder al subárbol izquierdo del nodo porque primero se debe acceder al subárbol derecho, por lo que el iterador - - llegará al nodo más a la derecha del subárbol izquierdo. Si el subárbol izquierdo está vacío, la puntuación se discutirá aquí . Si el nodo actual está a la izquierda del nodo principal, debe continuar ajustándose hacia arriba. Si el nodo actual está a la derecha del nodo principal, simplemente ajústelo hacia arriba una vez:

self& operator--()
{
    
    
	if (_node->left)//左边不为空
	{
    
    
		Node* mine = _node->left;
		while (mine->right)
		{
    
    
			mine = mine->right;
		}
		_node = mine;
	}	
	else//左边为空
	{
    
    
		Node* _parent = _node->parent;
		Node* cur = _node;
		while (_parent->left == cur && _parent != nullptr)
		{
    
    
			cur = _parent;
			_parent = _parent->parent;
		}
		_node = _parent;
	}
	return *this;
}

funciones de inicio y fin

La función de inicio del árbol rojo-negro devuelve el nodo más a la izquierda del árbol actual. La función de inicio se implementa en la clase de árbol rojo-negro, por lo que tenemos que agregar un tipo de iterador a la clase de árbol rojo-negro, y luego el El tipo de retorno de la función de inicio es un tipo de iterador, entonces el código aquí es el siguiente:

iterator begin()
{
    
    
	Node* cur = root;
	while (cur->left)
	{
    
    
		cur = cur->left;
	}
	return iterator(cur);	
}

La función final simplemente construye un iterador usando un puntero nulo y lo devuelve:

iterator end()
{
    
    
	return iterator(nullptr);
}

Una vez completado el iterador del árbol rojo-negro, se puede utilizar para encapsular los iteradores del mapa y configurar, luego el código aquí es el siguiente:

//set.h
template<class K>
class set
{
    
    
	struct SetofKey
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
public:
	typedef typename RBTree<K, K, SetofKey>::iterator iterator;
	iterator begin()
	{
    
    
		return tree.begin();
	}
	iterator end()
	{
    
    
		return tree.end();
	}
//....
private:
	RBTree<K, K, SetofKey> tree;
};
//map.h
template<class K, class V>
class map
{
    
    
	struct MapofKey
	{
    
    
		const K& operator()(const pair<const K, V>& kv)
		{
    
    
			return kv.first;
		}
	};
public:
	typedef typename RBTree<K, pair<const K, V>, MapofKey>::iterator iterator;
	iterator begin()
	{
    
    
		return tree.begin();
	}
	iterator end()
	{
    
    
		return tree.end();
	}
//....
private:
	RBTree<K, pair<const K, V>, MapofKey> tree;
};

Usamos el iterador de tipo en RBTree en el mapa y lo configuramos, pero no creamos una instancia de un objeto de árbol rojo-negro antes de usarlo, por lo que esto hará que el compilador no sepa si el iterador es un tipo o un tipo cuando usa este tipo de iterador. RBTree<K, pair<const K, V>, MapofKey>::iteratorUna variable estática, por lo que aquí tenemos que agregar un nombre de tipo para indicarle al compilador que, aunque aquí no se crea una instancia de ningún objeto, este iterador es un nombre de tipo.

prueba de código

Una vez completado el código anterior, podemos escribir un fragmento de código para probar la exactitud del código anterior. Primero, pruebe si la implementación de la función de inserción y la función de búsqueda del conjunto es correcta. Luego, el código de prueba aquí es el siguiente:

#include"set.h"
int main()
{
    
    
	srand(time(0));
	set<int> s1;
	const size_t N = 100000;
	for (size_t i = 0; i < N; ++i)
	{
    
    
		size_t x = rand();
		s1.insert(x);
	}
	s1.insert(100);
	cout << s1.check() << endl;
	cout << s1.find(100) << endl;
	return 0;
}

El resultado de ejecución del código es el siguiente:
Insertar descripción de la imagen aquí
cumple con nuestras expectativas, luego probemos si la implementación del iterador de set es verdadera, luego el código de prueba aquí es el siguiente:

int main()
{
    
    
	srand(time(0));
	set<int> s1;
	for (int i = 0; i < 100; i++)
	{
    
    
		int x = rand();
		s1.insert(x);
	}
	set<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
    
    
		cout << *it << " ";
		++it;
	}
	return 0;
}

El resultado de ejecución del código es el siguiente:
Insertar descripción de la imagen aquí
cumple con nuestras expectativas, por lo que esto muestra que el resultado de la implementación de nuestro código es correcto. A continuación, veamos si la implementación de la función de inserción y la función de búsqueda del mapa es correcta:

#include"set.h"
#include"map.h"
int main()
{
    
    
	map<int, int> m;
	srand(time(0));
	for (int i = 0; i <= 10000; i++)
	{
    
    
		int x = rand();
		m.insert(make_pair(x, x));
	}
	m.insert(make_pair(100, 100));
	cout << m.find(100) << endl;
	return 0;
}

Los resultados de ejecución del código son los siguientes:
Insertar descripción de la imagen aquí
Echemos un vistazo al código de prueba del iterador:

	map<int, int> m;
	srand(time(0));
	for (int i = 0; i <= 100; i++)
	{
    
    
		int x = rand();
		m.insert(make_pair(x, x));
	}
	map<int, int>::iterator it = m.begin();
	while (it != m.end())
	{
    
    
		cout << (*it).first << " ";
		++it;
	}

El resultado de ejecutar el código es el siguiente:

Insertar descripción de la imagen aquí
Si cumple con nuestras expectativas, significa que el contenido de la implementación actual del iterador del mapa es correcto, pero hay un problema en la implementación aquí. Cuando el iterador alcanza la posición final, el puntero interno _node apunta a nulo. En este momento, Si queremos permitirle volver a pasar, saldrá mal, entonces esto es un error en el iterador anterior. Si desea modificar este error, debe insertar un nodo principal en el árbol como una biblioteca. Así que aquí, sin modificaciones, veamos la implementación del iterador const.

iterador constante

Con la experiencia previa con iteradores de lista, sabemos que si queremos utilizar una plantilla de iterador para encapsular iteradores constantes e iteradores ordinarios, la plantilla de iterador requiere tres parámetros, como el siguiente código:

template<class V,class Ref,class Ptr>

El primer parámetro representa el tipo de datos señalado por el iterador actual, el segundo parámetro representa el tipo de datos de referencia y el tercer parámetro representa el tipo de datos del puntero. Después de modificar los parámetros de plantilla del iterador, tenemos que Modifique el cambio de nombre del tipo de iterador en el árbol rojo-negro, luego el código modificado aquí es el siguiente:

class RBTree
{
    
    
public:
//....
	typedef RBTreeNode<V> Node;
	typedef _RBTreeIterator<V, V&, V*> iterator;
	typedef _RBTreeIterator<V,const V&,const V*> const_iterator;
//....
}

Luego, el valor de retorno de la función debe modificarse en la clase iteradora. La sobrecarga del operador * devuelve una referencia al nodo, por lo que el valor de retorno de esta función se modifica a Ref, y la función de sobrecarga del operador -> devuelve la dirección del datos internos, por lo tanto, modifique su valor de retorno a Ptr, luego el código modificado es el siguiente:

template<class V,class Ref,class Ptr>
struct _RBTreeIterator
{
    
    
	typedef RBTreeNode<V> Node;
	Node* _node;
	_RBTreeIterator( Node* node)
		:_node(node)
	{
    
    }
	Ref operator++(){
    
    //...}
	Ref operator--(){
    
    //...}
	Ref operator*(){
    
    //...}
	Ptr operator->(){
    
    //...}
	bool operator !=(const self& s){
    
    //...}
};

Dado que el iterador interno ha cambiado, ¿es necesario modificar un poco el conjunto externo y el mapa? Primero, la clase de conjunto debe agregar versiones constantes de la función de inicio y la función de finalización, como el siguiente código:

template<class K>
class set
{
    
    
//...
public:
	typedef typename RBTree<K, K, SetofKey>::iterator iterator;
	typedef typename RBTree<K, K, SetofKey>::const_iterator const_iterator;
	iterator begin(){
    
    return tree.begin();}
	iterator end(){
    
    return tree.end();}
	const_iterator begin() const{
    
    return tree.begin();}
	const_iterator end() const{
    
    return tree.end();}
private:
	RBTree<K, K, SetofKey> tree;
};

Pero hay un problema con esta implementación. Sabemos que los datos dentro del conjunto están estrechamente relacionados. Si el valor de los datos internos se modifica a voluntad, se producirá un error en la relación y ya no se convertirá en un árbol binario de búsqueda. Por ejemplo, el siguiente código:

int main()
{
    
    
	set<int> s;
	s.insert(1);
	s.insert(2);
	s.insert(3);
	s.insert(4);
	set<int>::iterator it = s.begin();
	cout << *it << endl;
	*it = 10;
	cout << *it << endl;
	return 0;
}

El resultado de ejecución de este código es el siguiente:
Insertar descripción de la imagen aquí
Bien, ya sea un iterador constante o un iterador normal, los datos internos del conjunto no se pueden modificar, por lo que esencialmente no debería haber una versión normal del iterador, pero hay una en la biblioteca También hay iteradores ordinarios en los hábitos de uso diario, entonces, ¿cómo modificarlos aquí? Pero usar const iterator para encapsular el iterador ordinario de set. En la superficie, este es un iterador ordinario, pero en realidad es un iterador const. Sin embargo, dicha modificación causará otro problema. La versión ordinaria anterior de las funciones de inicio y fin son Ambos Todos los iteradores devueltos son iteradores ordinarios de árboles rojo-negro, pero después de la modificación anterior, el tipo de valor de retorno ahora se ha convertido en un iterador constante, por lo que habrá un problema aquí: no hay forma de convertir iteradores ordinarios en iteradores constantes. y que ¿Cuál es la solución? Pero simplemente elimine la versión normal de las funciones de inicio y fin, de modo que solo existan en la clase las versiones constantes de las funciones de inicio y fin, de modo que cuando se llame, se devuelvan todas las versiones constantes del iterador del árbol rojo-negro, luego el código aquí es el siguiente:

template<class K>
class set
{
    
    
//...
public:
	typedef typename RBTree<K, K, SetofKey>::const_iterator iterator;
	typedef typename RBTree<K, K, SetofKey>::const_iterator const_iterator;
	iterator begin() const{
    
    return tree.begin();}
	iterator end() const{
    
    return tree.end();}
	bool insert(const K& key){
    
    return tree.insert(key);}
	bool find(const K& key){
    
    return tree.find(key);}
	bool check(){
    
    return tree.IsBalance();}
private:
	RBTree<K, K, SetofKey> tree;
};

Después de dicha modificación, el código de prueba anterior no puede ejecutarse normalmente:
Insertar descripción de la imagen aquí
el contenedor del mapa no puede encapsular ambos tipos de iteradores en iteradores constantes como se indicó anteriormente, porque el mapa permite a los usuarios modificar el valor V en cada dato, por lo que en el mapa El iterador constante es el iterador constante en el árbol rojo-negro, y el iterador ordinario es el iterador ordinario en el árbol rojo-negro, y se deben proporcionar dos versiones diferentes de las funciones de inicio y fin, por lo que el código aquí es el siguiente:

template<class K, class V>
class map
{
    
    
//....
public:
	typedef typename RBTree<K, pair<const K, V>, MapofKey>::iterator iterator;
	typedef typename RBTree<K, pair<const K, V>, MapofKey>::const_iterator const_iterator;
	iterator begin(){
    
    return tree.begin();}
	iterator end(){
    
    return tree.end();}
	const_iterator begin() const{
    
    return tree.begin();}
	const_iterator end() const{
    
    return tree.end();}
	bool insert(const pair<K,V>& data) {
    
    return tree.insert(data);}
	bool find(const K& key)	{
    
    return tree.find(key);}
private:
	RBTree<K, pair<const K, V>, MapofKey> tree;
};

Podemos usar el siguiente código para probar:

int main()
{
    
    
	map<string, int> m;
	m.insert(make_pair("a", 1));
	m.insert(make_pair("b", 2));
	m.insert(make_pair("c", 3));
	m.insert(make_pair("d", 4));
	map<string, int>::iterator it = m.begin();
	cout << it->second << endl;
	it->second = 10;
	cout << it->second << endl;
	return 0;
}

El resultado de ejecutar este código es el siguiente:
Insertar descripción de la imagen aquí

Implementación de corchetes

Para implementar corchetes, tenemos que modificar el valor de retorno de la función de inserción para que devuelva par. Luego, las funciones de inserción en el mapa externo y los contenedores de conjuntos son las siguientes:

pair<iterator,bool> insert(const pair<K,V>& data)
{
    
    
	return tree.insert(data);
}

Luego modifique la función de inserción en el árbol rojo-negro, porque el iterador en el par apunta a la dirección de los datos insertados o la dirección del elemento existente, y porque la variable cur en la función de inserción seguirá el árbol rojo-negro Ajustado y modificado, para que podamos crear una variable para registrar la posición actual después de encontrar la posición insertada, de modo que sea fácil de regresar cuando se devuelva el valor. Si el elemento actual no existe, el valor del segundo elemento ser verdadero. Si el valor actualmente insertado existe, el valor del segundo elemento será falso. Entonces el código modificado aquí es el siguiente:

	pair<iterator,bool> insert(const V& _data)
	{
    
    
		if (root == nullptr)
		{
    
    
			root = new Node(_data);
			root->_col = BLACK;
			return make_pair(iterator(root), true);
		}
		KeyofT kot;
		Node* _cur = root;
		Node* _parent = nullptr;
		while (_cur != nullptr)
		{
    
    
			if (kot(_cur->data) > kot(_data))
			{
    
    
				_parent = _cur;
				_cur = _cur->left;
			}
			else if (kot(_cur->data) < kot(_data))
			{
    
    
				_parent = _cur;
				_cur = _cur->right;
			}
			else
			{
    
    
				return make_pair(iterator(_cur),false);
			}
		}
		_cur = new Node(_data);
		Node* newnode = _cur;
		//节点的调整
		//.....
		return make_pair(iterator(newnode)true);
	}

Después de esta modificación, podemos implementar la función de sobrecarga de corchetes. Primero, crea un par para recibir el valor de retorno de la función de inserción y luego devuelve el segundo elemento del primer elemento del par. Luego, el código aquí es el siguiente:

V& operator[](const K& key)
{
    
    
	pair<iterator, bool> ret = insert(make_pair(key, V()));
	return ret.first->second;
}

Pero este cambio causará problemas porque el par devuelto por la función de inserción de set contiene iteradores constantes, mientras que el par devuelto por la función de inserción del árbol rojo-negro contiene iteradores ordinarios. Los iteradores ordinarios no se pueden convertir en iteradores constantes, por lo que esto aparece aquí.problema, entonces la solución en la biblioteca es recibir primero el valor de retorno de la función de inserción del árbol rojo-negro. Por ejemplo, el siguiente código:

pair<iterator,bool> insert(const K& key)
{
    
    
	pair<typename RBTree<K, K, SetofKey>::iterator, bool> ret = tree.insert(key);
}

Luego tenemos que implementar una función. La función de esta función es utilizar un iterador ordinario para construir un iterador constante. El método de creación aquí es el siguiente:

template<class V,class Ref,class Ptr>
struct _RBTreeIterator
{
    
    
	typedef RBTreeNode<V> Node;
	typedef _RBTreeIterator<V, Ref, Ptr> self;
	typedef _RBTreeIterator<V, V&, V*> iterator;
	Node* _node;
	_RBTreeIterator( Node* node)
		:_node(node)
	{
    
    }
	_RBTreeIterator(const iterator& s)
		:_node(s._node)
	{
    
    }
//....
};

Creamos un nuevo tipo de iterador, que es un cambio de nombre del iterador, pero el parámetro de este iterador es V, lo que significa que el iterador es siempre un iterador normal, y también agregamos un constructor, pero este constructor El parámetro es un iterador ordinario. Si el iterador actual es un iterador ordinario, esta función es un constructor de copia. Si el iterador actual es un iterador constante, esta función significa usar un iterador ordinario para construir un iterador constante. Luego podemos usar el siguiente código para verificar:

int main()
{
    
    
	map<string,  int> m;
	m.insert(make_pair("a", 1));
	m.insert(make_pair("b", 2));
	m.insert(make_pair("c", 3));
	m.insert(make_pair("d", 4));
	map<string, int>::const_iterator it = m.begin();//编译器优化
	cout << it->second << endl;
	it->second = 10;
	cout << it->second << endl;
	return 0;
}

Aquí, el iterador ordinario se asigna a un iterador constante, y luego el iterador constante quiere modificar el valor. El resultado de este código es el siguiente: Obviamente, este código es
Insertar descripción de la imagen aquí
incorrecto, pero la razón del error es que el iterador constante no puede Si hay un error al realizar una modificación en lugar de un iterador normal para construir un iterador constante, entonces esto significa que la implementación de nuestro código es correcta y finalmente usamos el siguiente código para probar:

int main()
{
    
    
	string arr[] = {
    
     "苹果","苹果", "苹果", "苹果", "苹果",
				  "香蕉","香蕉", "香蕉", "香蕉", "香蕉",
					"西瓜","西瓜", "西瓜", "梨子", "梨子" };
	map<string, int> m;
	for (auto ch : arr)
	{
    
    
		m[ch]++;
	}
	for (auto ch : m)
	{
    
    
		cout << ch.first << ":" << ch.second << endl;
	}
	return 0;
}

El resultado de la ejecución del código es el siguiente:
Insertar descripción de la imagen aquí
cumple con nuestras expectativas, entonces esto demuestra que no hay ningún problema con la implementación de nuestro código, el código completo es el siguiente:
El código del archivo RBTree.h es el siguiente:

#pragma once
#include<iostream>
using namespace std;
enum colour
{
    
    
	RED,
	BLACK,
};
template<class T>
struct RBTreeNode
{
    
    
	RBTreeNode(const T& _data)
		:parent(nullptr)
		, right(nullptr)
		, left(nullptr)
		, data(_data)
		, _col(RED)
	{
    
    }
	RBTreeNode<T>* parent;
	RBTreeNode<T>* right;
	RBTreeNode<T>* left;
	T data;
	colour _col;
};
template<class V,class Ref,class Ptr>
struct _RBTreeIterator
{
    
    
	typedef RBTreeNode<V> Node;
	typedef _RBTreeIterator<V, Ref, Ptr> self;
	typedef _RBTreeIterator<V, V&, V*> iterator;
	Node* _node;

	_RBTreeIterator( Node* node)
		:_node(node)
	{
    
    }
	_RBTreeIterator(const iterator& s)
		:_node(s._node)
	{
    
    }
	self& operator++()
	{
    
    
		if (_node->right)
		{
    
    
			Node* mine = _node->right;
			while (mine->left)
			{
    
    
				mine = mine->left;
			}
			_node = mine;
		}
		else
		{
    
    
			Node* parent = _node->parent;
			Node* cur = _node;
			while (parent != nullptr && parent->right == cur)
			{
    
    
				cur = parent;
				parent = parent->parent;
			}
			_node = parent;
		}
		return *this;
	}
	Ref operator--()
	{
    
    
		if (_node->left)//左边不为空
		{
    
    
			Node* mine = _node->left;
			while (mine->right)
			{
    
    
				mine = mine->right;
			}
			_node = mine;
		}
		else//左边为空
		{
    
    
			Node* _parent = _node->parent;
			Node* cur = _node;
			while (_parent != nullptr&&_parent->left == cur )
			{
    
    
				cur = _parent;
				_parent = _parent->parent;
			}
			_node = _parent;
		}
		return *this;
	}
	Ref operator*()
	{
    
    
		return _node->data;
	}
	Ptr operator->()
	{
    
    
		return &_node->data;
	}
	bool operator !=(const self& s)
	{
    
    
		return _node != s._node;
	}
};
template<class K, class V, class KeyofT>
class RBTree
{
    
    

public:
	typedef RBTreeNode<V> Node;

	typedef _RBTreeIterator<V, V&, V*> iterator;
	typedef _RBTreeIterator<V,const V&,const V*> const_iterator;
	RBTree()
		:root(nullptr)
	{
    
    }
	iterator begin()
	{
    
    
		Node* cur = root;
		while (cur->left)
		{
    
    
			cur = cur->left;
		}
		return iterator(cur);
	}
	iterator end()
	{
    
    
		return iterator(nullptr);
	}
	pair<iterator,bool> insert(const V& _data)
	{
    
    
		if (root == nullptr)
		{
    
    
			root = new Node(_data);
			root->_col = BLACK;
			return make_pair(iterator(root), true);
		}
		KeyofT kot;
		Node* _cur = root;
		Node* _parent = nullptr;
		while (_cur != nullptr)
		{
    
    
			if (kot(_cur->data) > kot(_data))
			{
    
    
				_parent = _cur;
				_cur = _cur->left;
			}
			else if (kot(_cur->data) < kot(_data))
			{
    
    
				_parent = _cur;
				_cur = _cur->right;
			}
			else
			{
    
    
				return make_pair(iterator(_cur),false);
			}
		}
		_cur = new Node(_data);
		Node* newnode = _cur;
		if (kot(_data) > kot(_parent->data))
			//如果插入的数据比parent的数据大
		{
    
    
			_parent->right = _cur;
			_cur->parent = _parent;
		}
		else
			//如果插入的数据比parent的数据笑
		{
    
    
			_parent->left = _cur;
			_cur->parent = _parent;
		}
		while (_parent != nullptr && _parent->_col == RED)
		{
    
    
			Node* _grandparent = _parent->parent;
			if (_grandparent->left == _parent)
				//祖先节点的左边要调整
			{
    
    
				Node* uncle = _grandparent->right;//parent在左边所以uncle在右边
				//情况一
				if (uncle != nullptr && uncle->_col == RED)
				{
    
    
					uncle->_col = _parent->_col = BLACK;
					if (_grandparent->parent != nullptr)
					{
    
    
						_grandparent->_col = RED;
					}
					_cur = _grandparent;
					_parent = _grandparent->parent;
				}
				else
				{
    
    
					//情况二
					if (_cur == _parent->left)
					{
    
    
						RototalR(_grandparent);
						_parent->_col = BLACK;
						_grandparent->_col = RED;
					}
					else//情况三
					{
    
    
						RototalL(_parent);
						RototalR(_grandparent);
						_cur->_col = BLACK;
						_grandparent->_col = RED;
					}
					break;
				}
			}
			else
				//祖先节点的右边要调整
			{
    
    
				Node* uncle = _grandparent->left;//parent在右边所以uncle在左边
				//情况一
				if (uncle != nullptr && uncle->_col == RED)
				{
    
    
					uncle->_col = _parent->_col = BLACK;
					if (_grandparent->parent != nullptr)
					{
    
    
						_grandparent->_col = RED;
					}
					_cur = _grandparent;
					_parent = _grandparent->parent;
				}
				else
				{
    
    
					//情况二
					if (_cur == _parent->right)
					{
    
    
						RototalL(_grandparent);
						_parent->_col = BLACK;
						_grandparent->_col = RED;
					}
					else//情况三
					{
    
    
						RototalR(_parent);
						RototalL(_grandparent);
						_cur->_col = BLACK;
						_grandparent->_col = RED;
					}
					break;
				}
			}
		}
		return make_pair(iterator(newnode),true);
	}
	void inorder()
	{
    
    
		_inorder(root);
	}
	bool find(const K& val)
	{
    
    
		Node* cur = root;
		KeyofT kot;
		while (cur)
		{
    
    
			if ( kot(cur->data) == val)
			{
    
    
				return true;
			}
			else if (kot(cur->data) > val)
			{
    
    
				cur = cur->left;
			}
			else
			{
    
    
				cur = cur->right;
			}
		}
		return false;
	}
	bool check(Node* root, int BlackNums, int ref)
	{
    
    
		if (root == nullptr)
		{
    
    
			if (ref != BlackNums)
			{
    
    
				cout << "违反规则" << endl;
				return false;
			}
			return true;
		}
		if (root->_col == RED && root->parent->_col == RED)
		{
    
    
			cout << "出现了连续红色节点" << endl;
		}
		if (root->_col == BLACK)
		{
    
    
			BlackNums++;
		}
		return check(root->left, BlackNums, ref) && check(root->right, BlackNums, ref);
	}
	bool IsBalance()
	{
    
    
		if (root == nullptr)
		{
    
    
			return true;
		}
		if (root->_col != BLACK)
		{
    
    
			return false;
		}
		int ref = 0;
		Node* _left = root;
		while (_left)
		{
    
    
			if (_left->_col == BLACK)
			{
    
    
				ref++;
			}
			_left = _left->left;
		}
		return check(root, 0, ref);

	}
private:
	void _inorder(const Node* root)
	{
    
    
		if (root == nullptr)
		{
    
    
			return;
		}
		_inorder(root->left);
		cout << root->key << " ";
		_inorder(root->right);
	}
	void RototalL(Node* _parent)
	{
    
    
		Node* subR = _parent->right;//右孩子的节点
		Node* subRL = subR->left;//右孩子的左节点
		Node* ppNode = _parent->parent;//祖父节点
		//把subRL放到_parent的右
			_parent->right = subRL;
		if (subRL)
		{
    
    
			//如果subRL不为空则修改父节点的指向
				subRL->parent = _parent;
		}
		//把_parent放到subR的左
			subR->left = _parent;
		//修改_parent的parent的指向
			_parent->parent = subR;
		if (ppNode)//如果祖父不为空,则要改变祖父的指向
		{
    
    
			if (ppNode->right == _parent)
			{
    
    
				ppNode->right = subR;
				subR->parent = ppNode;
			}
			else//如果_parent是祖父的左
			{
    
    
				ppNode->left = subR;
				subR->parent = ppNode;
			}
		}
		else//祖父为空节点说明当前调整的是根节点
		{
    
    
			root = subR;
			subR->parent = nullptr;
		}
	}
	//右旋转
	void RototalR(Node* _parent)
	{
    
    
		Node* subL = _parent->left;
		Node* subLR = subL->right;
		Node* ppNode = _parent->parent;
		_parent->left = subLR;
		if (subLR)
		{
    
    
			subLR->parent = _parent;
		}
		subL->right = _parent;
		_parent->parent = subL;
		if (ppNode != nullptr)
		{
    
    
			if (ppNode->right == _parent)
			{
    
    
				ppNode->right = subL;
				subL->parent = ppNode;
			}
			else
			{
    
    
				ppNode->left = subL;
				subL->parent = ppNode;
			}
		}
		else
		{
    
    
			root = subL;
			subL->parent = nullptr;
		}
	}

	Node* root;
};

El código del archivo set.h es el siguiente:

#pragma once
#include"RBTree.h"
template<class K>
class set
{
    
    
	struct SetofKey
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
public:

	typedef typename RBTree<K, K, SetofKey>::const_iterator iterator;
	typedef typename RBTree<K, K, SetofKey>::const_iterator const_iterator;
	iterator begin() const
	{
    
    
		return tree.begin();
	}
	iterator end() const
	{
    
    
		return tree.end();
	}
	pair<iterator,bool> insert(const K& key)
	{
    
    
		pair<typename RBTree<K, K, SetofKey>::iterator, bool> ret = tree.insert(key);
	}
	bool find(const K& key)
	{
    
    
		return tree.find(key);
	}
	bool check()
	{
    
    
		return tree.IsBalance();
	}
private:
	RBTree<K, K, SetofKey> tree;
};

El código grande del archivo map.h es el siguiente:

#pragma once
#include"RBTree.h"
template<class K, class V>
class map
{
    
    
	struct MapofKey
	{
    
    
		const K& operator()(const pair<const K, V>& kv)
		{
    
    
			return kv.first;
		}
	};
public:
	typedef typename RBTree<K, pair<const K, V>, MapofKey>::iterator iterator;
	typedef typename RBTree<K, pair<const K, V>, MapofKey>::const_iterator const_iterator;
	iterator begin()
	{
    
    
		return tree.begin();
	}
	iterator end()
	{
    
    
		return tree.end();
	}
	const_iterator begin() const
	{
    
    
		return tree.begin();
	}
	const_iterator end() const
	{
    
    
		return tree.end();
	}
	pair<iterator,bool> insert(const pair<K,V>& data)
	{
    
    
		return tree.insert(data);
	}
	bool find(const K& key)
	{
    
    
		return tree.find(key);
	}
	V& operator[](const K& key)
	{
    
    
		pair<iterator, bool> ret = insert(make_pair(key, V()));
		return ret.first->second;
	}
private:
	RBTree<K, pair<const K, V>, MapofKey> tree;
};

Supongo que te gusta

Origin blog.csdn.net/qq_68695298/article/details/131277255
Recomendado
Clasificación