map和set的模拟实现

目录

map的模拟实现

map的基本框架

基础版本的map的插入和删除

map的迭代器以及map的begin()、end()等接口

map的operator【】 

最终版本的map的插入和删除

map的最终测试(涵盖上面所有的接口) 

map的整体代码

set的模拟实现 

set的整体代码

set的最终测试(涵盖上面所有的接口)


map的模拟实现

map的基本框架

map的底层就是一棵红黑树,所以基本框架如下。

#pragma once
#include"RBTree.h";

template<class Key,class T>//这里的T就是Value,写成T是为了和STL标准的一样
class Map
{
public:
	Map()
        :_rbt()
        {}
private:
	RBTree<Key,T> _rbt;
};

基础版本的map的插入和删除

前面说过,map的底层就是一棵红黑树,所以其插入删除都完全按照红黑树的插入删除的逻辑走,如果忘记了红黑树的相关细节,请回顾<<红黑树(RBTree)的模拟实现>>一文。

然后要说的是,因为红黑树是一棵搜索树,所以插入删除的逻辑都需要比较节点的大小,以此找到目标节点的位置。在<<红黑树(RBTree)的模拟实现>>一文中,RBTree这个类模板只有一个模板参数T,T是Node节点中存储的有效数据_data成员的类型,所以比较节点大小时是根据T类型的operator>等等relational operators成员函数比较,而我们知道map内部封装的红黑树中的节点所存储的有效数据是一个个pair,即T是pair,pair这个类自己支持operator>等等relational operators函数,所以pair可以根据这些relational operators函数比较大小,所以map中的红黑树中的节点就有了比较大小的依据,但问题来了,pair类自带的比较大小的函数的比较规则是:先根据first比较,谁的first大,谁就大;如果first相等,再根据second比较大小。可以发现这是不符合我们的期望的,因为在STL的map里,不管是插入还是删除,比较节点的大小时只看pair成员的key,也就是只看first,如果双方的first相等,那双方就直接判定为相等了,不受second影响。

综上所述,因为不符合我们的期望,所以需要对RBTree这个类模板稍作修改。如何修改呢?

1.首先将RBTree的模板参数从一个变成两个,即从RBTree<T>变成RBTree<Key,Value>。

问题:有人说这不是多此一举吗?比如说Key是int,Value是double,那RBTree模板参数只有一个T时,给T传pair<int,double>不就行了,干嘛增加一个模板参数Value呢?

答案:如果只看map的Insert,因为插入的数据是pair<int,double>类型的对象,那只要一个模板参数T是没问题的;但map还有删除Erase,Erase的参数的类型不是pair<int,double>,而是int类型的变量,这时如果只有一个模板参数T,即pair<int,double>,是拿不到int这个类型的,所以才需要Key和Value两个模板参数。

2.然后把Insert和Erase接口中比较节点的有效数据(T _data)的地方都作修改,比如都从data1<data2变成data1.first<data2.first,这个工作并不复杂。

稍作修改后的RBTree的代码如下。

template<class Key,class Value>//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
class RBTree
{
	typedef RBTreeNode<pair<Key,Value>> Node;
public:
	RBTree()
		:_root(nullptr)
	{}

	bool Erase(const Key& k)//RBTree作为map或者set的底层容器时,T一定是pair类模板实例化出的类
	{
		if (_root == nullptr)
		{
			cout << "树中已经不存在节点了,无法继续删除" << endl;
			return false;
		}
		Node* cur = _root;
		Node* curParent = cur->_parent;
		while (cur != nullptr)
		{
			//if (x < cur->_data)			
			if (k < cur->_data.first)
			{
				curParent = cur;
				cur = cur->_left;
			}
			//else if (x > cur->_data)
			else if (k > cur->_data.first)
			{
				curParent = cur;
				cur = cur->_right;
			}
			//找到了要删除的目标节点,开始删除
			else
			{
				/*
                这部分代码不需要进行修改,并且篇幅很大,所以省略了,如果有需要,请去RBTree一文中拷贝
                */
							
			}
		}
		//没有在树中找到目标节点,说明目标节点不存在,直接return false
		cout << "你要删除的节点不存在" << endl;
		return false;
	}

	bool Insert(const pair<Key, Value>& x)//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
	{
		if (_root == nullptr)
		{
			_root = new Node(x, black);
			return true;
		}
		else
		{
			Node* cur = _root;
			Node* curParent = nullptr;
			while (cur != nullptr)
			{
				//if (x > cur->_data)
				if (x.first > cur->_data.first)
				{
					curParent = cur;
					cur = cur->_right;
				}
				//else if (x < cur->_data)
				else if (x.first < cur->_data.first)
				{
					curParent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			//找到了nullptr空位置,开始插入
			
			//if (x > curParent->_data)
			if (x.first > curParent->_data.first)
			{
				cur = new Node(x, red);
				curParent->_right = cur;
				cur->_parent = curParent;
			}
			else
			{
				cur = new Node(x, red);
				curParent->_left = cur;
				cur->_parent = curParent;
			}
			//插入新节点成功,开始控制平衡

			/*
                这部分代码不需要进行修改,并且篇幅很大,所以省略了,如果有需要,请去RBTree一文中拷贝
            */

		}
	}
	
private:
	Node* _root;
};

RBTree的代码修改完毕后,map的Insert和Erase就简单了,如下代码,只是一层极其简单的封装。(注意下面map的Insert并不是最终版本,在下文中还会做修改)

说一下,STL标准的map的Erase接口是没有返回值的,但RBTree的Erase又有返回值,我们决定这里不对RBTree的返回值作修改了,不用从bool改成void,因为没有必要,直接改map的Erase的返回值类型即可,从bool改成void,如下所示。

#pragma once
#include"RBTree.h";


template<class Key,class T>//这里的T就是value,写成T是为了和STL标准的一样
class Map
{
public:
	Map()
		:_rbt()
	{}
	bool Insert(const pair<Key, T>& p)
	{
		return _rbt.Insert(p);
	}

	void Erase(const Key& k)
	{
		_rbt.Erase(k);
	}
private:
	RBTree<Key,T> _rbt;
};

map的迭代器以及map的begin()、end()等接口

map这个容器就等价于红黑树,只不过树的节点是一个个pair,既然map等价于红黑树,所以map的迭代器就是RBTree的迭代器,直接拷贝RBTree的迭代器的代码即可,不需要作任何修改,并且因为map等价于红黑树,所以map的begin()等接口就等价于RBTree的begin()等接口,所以实现map的begin()等接口就是对RBTree的begin()等接口套一层封装。以下代码就是所有和map(即红黑树)的迭代器相关的代码。

注意为什么RBTreeIterator有两个模板参数T1、T2呢?直接一个T不行吗?答案是不行,原因在下面代码的const_iterator begin()const这个接口的注释中。

template<class T1,class T2>
struct RBTreeIterator
{
	typedef RBTreeNode<T1> Node;
	RBTreeIterator()
		:_n(nullptr)
	{}
	RBTreeIterator(Node* n)
		:_n(n)
	{}

	T2& operator*()
	{
		return (*_n)._data;
	}

	T2* operator->()
	{
		return &((*_n)._data);
	}

	bool operator==(const RBTreeIterator it)const //加const是为了让被const iterator也能调用该接口,注意区分const iterator和const_iterator,前者不是const迭代器
	{
		return _n == it._n;
	}

	bool operator!=(const RBTreeIterator it)const //加const是为了让被const iterator也能调用该接口,注意区分const iterator和const_iterator,前者不是const迭代器
	{
		return !(operator==(it));
	}


	RBTreeIterator operator++()//前置++
	{
		if (_n->_right == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_left)
				{
					_n = curParent;
					return (*this);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return (*this);
		}
		else
		{
			Node* Rmin = _n->_right;
			while (Rmin->_left != nullptr)
			{
				Rmin = Rmin->_left;
			}
			_n = Rmin;
			return (*this);
		}
	}

	RBTreeIterator operator++(int)//后置++
	{
		Node* temp = _n;
		if (_n->_right == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_left)
				{
					_n = curParent;
					return RBTreeIterator(temp);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return
			_n = nullptr;
			return RBTreeIterator(temp);
		}
		else
		{
			Node* Rmin = _n->_right;
			while (Rmin->_left != nullptr)
			{
				Rmin = Rmin->_left;
			}
			_n = Rmin;
			return RBTreeIterator(temp);
		}
	}

	RBTreeIterator operator--()//前置--
	{
		if (_n->_left == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_right)
				{
					_n = curParent;
					return (*this);//this指针的类型是RBTreeIterator<T>*, this指针的值是调用operator++接口的迭代器对象的地址
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return (*this);
		}
		else
		{
			Node* Lmax = _n->_left;
			while (Lmax->_right != nullptr)
			{
				Lmax = Lmax->_right;
			}
			_n = Lmax;
			return (*this);
		}
	}

	RBTreeIterator operator--(int)//后置--
	{
		Node* temp = _n;
		if (_n->_left == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_right)
				{
					_n = curParent;
					return RBTreeIterator(temp);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return RBTreeIterator(temp);
		}
		else
		{
			Node* Lmax = _n->_left;
			while (Lmax->_right != nullptr)
			{
				Lmax = Lmax->_right;
			}
			_n = Lmax;
			return RBTreeIterator(temp);
		}
	}

	
	Node* _n;
};


template<class Key,class Value>//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
class RBTree
{
	typedef RBTreeNode<pair<Key,Value>> Node;
public:
	typedef RBTreeIterator<pair<Key,Value>, pair<Key, Value>> iterator;
	typedef RBTreeIterator<pair<Key, Value>,const pair<Key, Value>> const_iterator;

	RBTree()
		:_root(nullptr)
	{}

	iterator begin()
	{
		if (_root == nullptr)
			return iterator(nullptr);
		else
		{
			Node* cur = _root;
			while (cur->_left != nullptr)
				cur = cur->_left;
			return iterator(cur);
		}
	}

	const_iterator begin()const
	{
		if (_root == nullptr)
			return const_iterator(nullptr);
		else
		{
			Node* cur = _root;
			while (cur->_left != nullptr)
				cur = cur->_left;
			return const_iterator(cur);//为什么RBTreeIterator<T1,T2>有两个模板参数T1、T2呢?
									   //如果RBTreeIterator<T>只有一个模板参数T,本行代码就会出问题,无法通过cur构造const_iterator。因为在当前begin函数中,cur的类型是RBTreeNode<T>*,
									   //而在const_iterator类中,即RBTreeIterator<const T>类中,const_iterator类的构造函数,即RBTreeIterator<const T>类的构造函数的参数的类型则是RBTreeNode<const T>*,
									   //因为cur的类型RBTreeNode<T>* 和const_iterator的构造函数的参数的类型RBTreeNode<const T>*不同,所以构造函数就会出现问题,所以RBTreeIterator<T>只有一个模板参数T是不行的。					
		}
	}

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

	const_iterator end()const
	{
		return const_iterator(nullptr);
	}
private:
	Node* _root;
};


template<class Key,class T>//这里的T就是value,写成T是为了和STL标准的一样
class Map
{
public:
    //文中说过,map就等价于红黑树,所以红黑树的迭代器就是map的迭代器
	typedef RBTreeIterator<pair<Key, T>, pair<Key, T>> iterator;
	typedef RBTreeIterator<pair<Key, T>, const pair<Key, T>> const_iterator;

	Map()
		:_rbt()
	{}

	iterator begin()
	{
		return _rbt.begin();
	}
	
	const_iterator begin()const
	{
		return _rbt.begin();
	}

	iterator end()
	{
		return _rbt.end();
	}

	const_iterator end()const
	{
		return _rbt.end();
	}

private:
	RBTree<Key,T> _rbt;
};


map的operator【】 

为了实现map的Insert,在上文中我们已经对RBTree的Insert做过稍许修改,现在如果要实现map的operator【】,还要在之前的基础上,再把RBTree的Insert的返回值的类型做修改,将返回值从bool变成pair<iterator,bool>,然后把return的返回值也改一改,将bool值变成pair<iterator,bool>的对象。因为对于标准的map的operator【】来说,如果红黑树中已经存在了需要插入的目标节点,则不再继续插入,但要返回一个pair,first成员是指向目标节点的迭代器,second成员是bool值,在当前情况下为false;如果黑树中不存在需要插入的目标节点,则要完成插入,然后再返回一个pair,first成员是指向刚插入的目标节点的迭代器,second成员是bool值,在当前情况下为true。

说一下,STL标准的map的Erase接口是没有返回值的,所以RBTree的Erase我们无需进行修改,只需要改RBTree的Insert。

按照上面理论,Insert的代码被修改后,如下所示。

template<class Key,class Value>//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
class RBTree
{
	typedef RBTreeNode<pair<Key,Value>> Node;
public:
	typedef RBTreeIterator<pair<Key,Value>, pair<Key, Value>> iterator;
	typedef RBTreeIterator<pair<Key, Value>,const pair<Key, Value>> const_iterator;

	RBTree()
		:_root(nullptr)
	{}

    pair<iterator,bool> Insert(const pair<Key, Value>& x)//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
	{
		if (_root == nullptr)
		{
			_root = new Node(x, black);
			//return true;
			return make_pair(iterator(_root), true);
		}
		else
		{
			Node* cur = _root;
			Node* curParent = nullptr;
			while (cur != nullptr)
			{
				//if (x > cur->_data)
				if (x.first > cur->_data.first)
				{
					curParent = cur;
					cur = cur->_right;
				}
				//else if (x < cur->_data)
				else if (x.first < cur->_data.first)
				{
					curParent = cur;
					cur = cur->_left;
				}
				else
				{
					//return false;
					return make_pair(iterator(cur), false);
				}
			}
			//找到了nullptr空位置,开始插入
			
			//if (x > curParent->_data)
			if (x.first > curParent->_data.first)
			{
				cur = new Node(x, red);
				curParent->_right = cur;
				cur->_parent = curParent;
			}
			else
			{
				cur = new Node(x, red);
				curParent->_left = cur;
				cur->_parent = curParent;
			}
			//插入新节点成功,开始控制平衡

			Node* temp = cur;//因为下面发生变色调整或者旋转后,cur指针可能会继续往上走,继续进行调整,从而可能让cur指针不再指向新插入的节点,但在调整完毕后return返回值时,是需要返回指向新插入节点的迭代器的,所以这里设置一个temp指针记录新插入节点的地址,防止找不到新插入的节点
			Node* parent = nullptr;
			Node* grandParent = nullptr;
			while (cur->_col == red)//如果调整到根节点来了,就不能再调整了。因为根节点的颜色必须是黑色,所以这里拿cur的颜色是红色作为继续循环的条件
			{
				parent = cur->_parent;//父亲节点不可能为nullptr,因此下一行代码无需判空
				grandParent = parent->_parent;//注意grandParent可能为nullptr

				//如果cur的爷爷节点存在,并且cur和parent是连续红色节点,此时才需要变色控制平衡;否则不需要控制平衡,此时插入新节点成功,直接就可以结束Insert函数了
				if (grandParent != nullptr && (cur->_col == red && parent->_col == red))
				{
					//当cur与parent节点都在grandParent的左子树上时走这里
					if (parent == grandParent->_left)
					{
						Node* uncle = grandParent->_right;

						//第一种情况,此时无所谓cur与parent和grandParent是否为一条直线,但要求parent为红,uncle存在且为红
						//		g
						//	  p	  u
						//  c
						if (parent->_col == red && uncle != nullptr && uncle->_col == red)
						{
							parent->_col = black;
							uncle->_col = black;
							if (grandParent != _root)
								grandParent->_col = red;
							cur = grandParent;
						}
						else if ((parent->_col == red && uncle == nullptr) || (parent->_col == red && uncle != nullptr && uncle->_col == black))
						{
							//第二种情况,此时cur与parent和grandParent是一条直线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  p	  u  
							//  c			
							if (cur == parent->_left)
							{
								RotateR(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp),true);
							}

							//第三种情况,此时cur与parent和grandParent是一条折线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  p	  u  
							//     c		
							else if (cur == parent->_right)
							{
								//先对以p为根节点的树进行左单旋,旋转完毕后将parent指针和cur指针的指向交换,此时节点之间就形成了和第二种情况完全一致的形状,接下来就照着第二种情况的做法去做即可控制平衡。

								//先左单旋
								RotateL(parent);
								swap(parent, cur);//注意一定要交换两个指针的指向,否则本行下面第4行和第5行的代码(即变色的代码)会出问题,会给节点变错颜色。

								//然后按照第二种情况的做法去做
								RotateR(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
						}
					}
					//当cur与parent节点都在grandParent的右子树上时走这里
					else if (parent == grandParent->_right)
					{
						Node* uncle = grandParent->_left;

						//第一种情况,此时无所谓cur与parent和grandParent是否为一条直线,但要求parent为红,uncle存在且为红
						//		g				
						//	  u	  p     	
						//          c
						if (parent->_col == red && uncle != nullptr && uncle->_col == red)
						{
							parent->_col = black;
							uncle->_col = black;
							if (grandParent != _root)
								grandParent->_col = red;
							cur = grandParent;
						}

						else if ((parent->_col == red && uncle == nullptr) || (parent->_col == red && uncle != nullptr && uncle->_col == black))
						{
							//第二种情况,此时cur与parent和grandParent是一条直线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  u	  p  
							//          c
							if (cur == parent->_right)
							{
								RotateL(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
							//第三种情况,此时cur与parent和grandParent是一条折线,parent为红,uncle不存在或者存在且为黑
							//	    g	
							//	  u   p  
							//       c		
							else if (cur == parent->_left)
							{
								//先对以p为根节点的树进行右单旋,旋转完毕后将parent指针和cur指针的指向交换,此时节点之间就形成了和第二种情况完全一致的形状,接下来就照着第二种情况的做法去做即可控制平衡。

								//先右单旋
								RotateR(parent);
								swap(parent, cur);//注意一定要交换两个指针的指向,否则本行下面第4行和第5行的代码(即变色的代码)会出问题,会给节点变错颜色。

								//然后按照第二种情况的做法去做
								RotateL(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
						}
					}
				}
				//如果cur的爷爷节点不存在,或者爷爷节点存在但cur和parent节点不同时为红色,此时不需要变色控制平衡,插入函数直接就可以结束了
				else//(grandParent == nullptr||(grandParent != nullptr && (cur->_col == black || parent->_col == black)))
				{
					//return true;
					return make_pair(iterator(temp), true);
				}
			}
            //如果cur指针一直向上调整到了根节点,根节点被调整完毕后就会走出循环,出了循环后也要return true,不要忘记了
            //return true;
			return make_pair(iterator(temp), true);
		}
	}
private:
	Node* _root;
};

修改完RBTree的Insert后,接下来对map的Insert也需要进行稍微的修改,只改返回值的类型即可,如下代码所示,下面的代码就是最终版本的map的插入和删除了。可以发现,map真的就等价于红黑树,只不过map这棵红黑树上挂的节点是一个个pair,除此之外再无区别,比如map的插入就是调用红黑树的插入,map的删除也就是调用红黑树的删除。

最终版本的map的插入和删除

#pragma once
#include"RBTree.h";

template<class Key,class T>//这里的T就是value,写成T是为了和STL标准的一样
class Map
{
public:
	//文中说过,map就等价于红黑树,所以红黑树的迭代器就是map的迭代器
	typedef RBTreeIterator<pair<Key, T>, pair<Key, T>> iterator;
	typedef RBTreeIterator<pair<Key, T>, const pair<Key, T>> const_iterator;

	Map()
		:_rbt()
	{}

	pair<iterator ,bool> Insert(const pair<Key, T>& p)
	{
		return _rbt.Insert(p);
	}

	void Erase(const Key& k)
	{
		_rbt.Erase(k);
	}

private:
	RBTree<Key,T> _rbt;
};

map的最终版本的插入实现完毕后,就可以继续实现map的operator【】了,思路为:在map的operator【】函数中调用红黑树的Insert(或者map的Insert也行),插入一个pair对象A,first成员的值是用户传递的k,second成员的值等于匿名的T对象调用默认构造后的值,如果map(红黑树)中已经存在和要插入的pair对象A的first成员值相等的pair对象B了,那么不再插入A,返回一个pair对象C,first成员是指向pair对象B的迭代器,second成员是false;如果map(红黑树)中不存在和要插入的pair对象A的first成员值相等的pair对象B,则插入A,返回一个pair对象C,first成员是指向pair对象A的迭代器,second成员是true。最后根据前面是哪一种情况,最后在map的operator【】函数中,return一个通过【解引用pair对象C的first成员(即迭代器)】得到的pair对象A或者B的second成员,也就是KV模型中的Value值的引用。

根据上面的思路,代码如下。

#pragma once
#include"RBTree.h";



template<class Key,class T>//这里的T就是value,写成T是为了和STL标准的一样
class Map
{
public:
    //文中说过,map就等价于红黑树,所以红黑树的迭代器就是map的迭代器
	typedef RBTreeIterator<pair<Key, T>, pair<Key, T>> iterator;
	typedef RBTreeIterator<pair<Key, T>, const pair<Key, T>> const_iterator;

	Map()
		:_rbt()
	{}

	T& operator[](const Key& k)
	{
		pair<iterator, bool> temp = _rbt.Insert(make_pair(k, T()));
		return (*(temp.first)).second;
	}
private:
	RBTree<Key,T> _rbt;
};

map的最终测试(涵盖上面所有的接口) 

测试如下图,可以发现是符合我们的预期的。

上图代码如下。

#include"Set.h";
#include"Map.h";

void test2()
{
	string s[] = { "苹果","香蕉","牛奶","香蕉","橙子","苹果","菠萝"};
	Map<string, int>m;
	
	//对map的插入的检测如下
	for (string& e : s)
	{
		m[e]++;//因为map的operator[]是对map的Insert(或者说是对RBTree的Insert)的一层封装,所以如果检测时发现operator[]没有问题,那map的Insert也肯定是没问题的
	}

	//对map的迭代器部分、map的begin()、end()的检测如下
	for (Map<string, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << it->first << ":" << it->second<<endl;
	}
	cout << endl;

	//对map的删除的检测如下
	m.Erase("香蕉");
	m.Erase("牛奶");
	m.Erase("菠萝");

	//支持map的迭代器后就支持范围for了
	for (pair<string, int>& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;

}
void main()
{
	test2();
}

map的整体代码

RBTree.h的代码如下。

再次说明,下面RBTree.h的代码是专门提供给Map和Set的底层的,和<<红黑树(RBTree)的模拟实现>>一文中的正常的RBTree.h的代码不完全一样,是经过了稍微的修改的,至于修改了哪些内容,在上文中已经全部说过了。


/*
	注意这个版本的RBTree.h是提供给Map和Set的底层的,和正常的RBTree不一样
*/


#pragma once
#include<iostream>
using namespace std;

enum color
{
	red,
	black
};

template<class T>
class RBTreeNode
{
public:
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	color _col;

	RBTreeNode()
		:_data()
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col()
	{}

	RBTreeNode(const T& x, color col)
		:_data(x)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(col)
	{}
};

template<class T1,class T2>
struct RBTreeIterator
{
	typedef RBTreeNode<T1> Node;
	RBTreeIterator()
		:_n(nullptr)
	{}
	RBTreeIterator(Node* n)
		:_n(n)
	{}

	T2& operator*()
	{
		return (*_n)._data;
	}

	T2* operator->()
	{
		return &((*_n)._data);
	}

	bool operator==(const RBTreeIterator it)const //加const是为了让被const iterator也能调用该接口,注意区分const iterator和const_iterator,前者不是const迭代器
	{
		return _n == it._n;
	}

	bool operator!=(const RBTreeIterator it)const //加const是为了让被const iterator也能调用该接口,注意区分const iterator和const_iterator,前者不是const迭代器
	{
		return !(operator==(it));
	}


	RBTreeIterator operator++()//前置++
	{
		if (_n->_right == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_left)
				{
					_n = curParent;
					return (*this);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return (*this);
		}
		else
		{
			Node* Rmin = _n->_right;
			while (Rmin->_left != nullptr)
			{
				Rmin = Rmin->_left;
			}
			_n = Rmin;
			return (*this);
		}
	}

	RBTreeIterator operator++(int)//后置++
	{
		Node* temp = _n;
		if (_n->_right == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_left)
				{
					_n = curParent;
					return RBTreeIterator(temp);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return
			_n = nullptr;
			return RBTreeIterator(temp);
		}
		else
		{
			Node* Rmin = _n->_right;
			while (Rmin->_left != nullptr)
			{
				Rmin = Rmin->_left;
			}
			_n = Rmin;
			return RBTreeIterator(temp);
		}
	}

	RBTreeIterator operator--()//前置--
	{
		if (_n->_left == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_right)
				{
					_n = curParent;
					return (*this);//this指针的类型是RBTreeIterator<T>*, this指针的值是调用operator++接口的迭代器对象的地址
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return (*this);
		}
		else
		{
			Node* Lmax = _n->_left;
			while (Lmax->_right != nullptr)
			{
				Lmax = Lmax->_right;
			}
			_n = Lmax;
			return (*this);
		}
	}

	RBTreeIterator operator--(int)//后置--
	{
		Node* temp = _n;
		if (_n->_left == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_right)
				{
					_n = curParent;
					return RBTreeIterator(temp);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return RBTreeIterator(temp);
		}
		else
		{
			Node* Lmax = _n->_left;
			while (Lmax->_right != nullptr)
			{
				Lmax = Lmax->_right;
			}
			_n = Lmax;
			return RBTreeIterator(temp);
		}
	}

	
	Node* _n;
};

template<class Key,class Value>//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
class RBTree
{
	typedef RBTreeNode<pair<Key,Value>> Node;
public:
	typedef RBTreeIterator<pair<Key,Value>, pair<Key, Value>> iterator;
	typedef RBTreeIterator<pair<Key, Value>,const pair<Key, Value>> const_iterator;

	RBTree()
		:_root(nullptr)
	{}

	iterator begin()
	{
		if (_root == nullptr)
			return iterator(nullptr);
		else
		{
			Node* cur = _root;
			while (cur->_left != nullptr)
				cur = cur->_left;
			return iterator(cur);
		}
	}

	const_iterator begin()const
	{
		if (_root == nullptr)
			return const_iterator(nullptr);
		else
		{
			Node* cur = _root;
			while (cur->_left != nullptr)
				cur = cur->_left;
			return const_iterator(cur);
		}
	}

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

	const_iterator end()const
	{
		return const_iterator(nullptr);
	}


	bool Erase(const Key& k)//RBTree作为map或者set的底层容器时,T一定是pair类模板实例化出的类
	{
		if (_root == nullptr)
		{
			cout << "树中已经不存在节点了,无法继续删除" << endl;
			return false;
		}
		Node* cur = _root;
		Node* curParent = cur->_parent;
		while (cur != nullptr)
		{
			//if (x < cur->_data)			
			if (k < cur->_data.first)
			{
				curParent = cur;
				cur = cur->_left;
			}
			//else if (x > cur->_data)
			else if (k > cur->_data.first)
			{
				curParent = cur;
				cur = cur->_right;
			}
			//找到了要删除的目标节点,开始删除
			else
			{
				//第一种情况,如果目标节点没有孩子节点
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					//对应文中的1.1 —— 如果删除的节点是红色叶子节点
					if (cur->_col == red)
					{
						if (cur == curParent->_left)
							curParent->_left = nullptr;//删除cur前,记得断开curParent与cur的连接关系
						else
							curParent->_right = nullptr;//删除cur前,记得断开curParent与cur的连接关系
						delete cur;
						return true;
					}
					//对应文中的1.2 —— 如果删除的节点是黑色,且没有孩子节点,则删除后需要调整平衡
					else
					{
						//情形1:要删除的目标节点cur是根节点,所以删除后无需调整平衡
						if (cur == _root)
						{
							delete cur;
							_root = nullptr;
							return true;
						}

						// 情形2:被删除节点不是根节点,需要进行调整平衡,注意,在向上调整时,不管cur这个黑色节点的是否存在孩子节点,我们都把cur当作没有孩子节点
						int flag = 0;//用于在向上迭代调整的时候,控制不要把cur删除
						while (cur != _root)
						{
							//情形2.2:如果cur是curParent的右孩子
							if (cur == curParent->_right)
							{
								Node* curBrother = curParent->_left;//无需判空,因为cur不是根节点,所以curParent一定不为空
								//情形2.2.1:如果cur的兄弟是黑色节点
								if (curBrother->_col == black)
								{
									//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
									//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
									if (curBrother->_left != nullptr && curBrother->_left->_col == red)
									{
										/*
										下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
										方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
										*/
										Node* curNephewL = curBrother->_left;
										RotateR(curParent);
										curBrother->_col = curParent->_col;
										curParent->_col = black;
										curNephewL->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
									else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
									{
										/*
										下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
										方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
										*/
										Node* curNephewR = curBrother->_right;
										RotateL(curBrother);
										RotateR(curParent);
										curNephewR->_col = curParent->_col;
										curParent->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
									//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
									else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
										|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
									{
										//情形2.2.1.4.1:如果父亲节点是红色
										if (curParent->_col == red)
										{
											curParent->_col = black;
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
										else
										{
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
											cur = curParent;
											curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
										}
									}
								}
								//情形2.2.2:如果cur的兄弟curBrother是红色节点,则说明curBrother一定有左右孩子节点,并且都是黑色
								else
								{
									//当前情况cur是curParent的右孩子,curBrother是curParent的左孩子,所以直接对curParent右单旋
									RotateR(curParent);
									swap(curBrother->_col, curParent->_col);
									//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
									curBrother = curParent->_left;
									//走到这里,情况会转换成2.2.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.2.1的代码

									//情形2.2.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewL->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
										else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curBrother);
											RotateR(curParent);
											curNephewR->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.2.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
								}
							}
							//情形2.1:如果cur是curParent的左孩子
							else
							{
								Node* curBrother = curParent->_right;//无需判空,因为cur不是根节点,所以curParent一定不为空
								//情形2.1.1:如果cur的兄弟是黑色节点
								if (curBrother->_col == black)
								{
									//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
									//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
									if (curBrother->_right != nullptr && curBrother->_right->_col == red)
									{
										/*
										下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
										方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
										*/
										Node* curNephewR = curBrother->_right;
										RotateL(curParent);
										curBrother->_col = curParent->_col;
										curParent->_col = black;
										curNephewR->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
									else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
									{
										/*
										下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
										方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
										*/
										Node* curNephewL = curBrother->_left;
										RotateR(curBrother);
										RotateL(curParent);
										curNephewL->_col = curParent->_col;
										curParent->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.1.1.4:如果cur的兄弟没有子节点走这里
									//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
									else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
										|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
									{
										//情形2.1.1.4.1:如果父亲节点是红色
										if (curParent->_col == red)
										{
											curParent->_col = black;
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
										else
										{
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
											cur = curParent;
											curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
										}
									}
								}
								//情形2.1.2:如果cur的兄弟是红色节点
								else
								{
									//当前情况cur是curParent的左孩子,所以curBrother一定是curParent的右孩子,所以直接对curParent左单旋
									RotateL(curParent);
									swap(curBrother->_col, curParent->_col);
									//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
									curBrother = curParent->_right;
									//走到这里,情况会转换成2.1.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.1.1的代码

									//情形2.1.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
										if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewR->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curBrother);
											RotateL(curParent);
											curNephewL->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.4:如果cur的兄弟没有子节点走这里
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.1.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
								}
							}
						}
						//走到这里,即走出了用于向上调整的循环,说明是向上调整到了根节点,此时直接return true即可
						return true;
					}
				}
				//第二种情况,如果目标节点只有左孩子
				else if (cur->_left != nullptr && cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
						_root->_parent = nullptr;//不要忘了修改新根节点的parent指针的指向
						_root->_col = black;//如果要删除的目标节点是整棵树的根节点,并且还只有左孩子,则说明左孩子一定为红,此时一定不要忘了的修改新根节点的颜色,把他从红色变成黑色
						delete cur;
						return true;
					}
					else
					{
						if (cur == curParent->_left)
						{
							curParent->_left = cur->_left;
							cur->_left->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						else
						{
							curParent->_right = cur->_left;
							cur->_left->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						cur->_left->_col = black;
						delete cur;
						return true;
					}
				}
				//第三种情况,如果目标节点只有右孩子
				else if (cur->_left == nullptr && cur->_right != nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
						_root->_parent = nullptr;//不要忘了修改新根节点的parent指针的指向
						_root->_col = black;//如果要删除的目标节点是整棵树的根节点,并且还只有右孩子,则说明右孩子一定为红,此时一定不要忘了的修改新根节点的颜色,把他从红色变成黑色
						delete cur;
						return true;
					}
					else
					{
						if (cur == curParent->_left)
						{
							curParent->_left = cur->_right;
							cur->_right->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						else
						{
							curParent->_right = cur->_right;
							cur->_right->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						cur->_right->_col = black;
						delete cur;
						return true;
					}
				}
				//第四种情况,如果目标节点既有左孩子,又有右孩子
				else if (cur->_left != nullptr && cur->_right != nullptr)
				{
					Node* Rmin = cur->_right;
					Node* RminParent = cur;
					while (Rmin->_left != nullptr)
					{
						RminParent = Rmin;
						Rmin = Rmin->_left;
					}
					cur->_data = Rmin->_data;
					//走到这里,替死鬼节点Rmin就被找到了,Rmin只有两种可能,第一:只有右孩子,第二:没有孩子节点

					//如果只有右孩子,则套用上面的第三种情况的思路即可。
					if (Rmin->_right != nullptr)
					{
						//注意Rmin不可能是整棵树的根节点,所以不必一板一眼的套用第三种情况的思路
						if (Rmin == RminParent->_left)
						{
							RminParent->_left = Rmin->_right;
							Rmin->_right->_parent = RminParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						else
						{
							RminParent->_right = Rmin->_right;
							Rmin->_right->_parent = RminParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						Rmin->_right->_col = black;
						delete Rmin;
						return true;
					}
					//替死鬼节点Rmin没有孩子节点,此时直接套用第一种情况的思路即可,但注意,因为第一种情况的代码量太大了,这里我们就不套用思路了,而是直接拷贝第一种情况的代码
					else
					{
						//先把Rmin和RminParent指针的值赋给cur与curParent,这样我们就可以直接拷贝第一种情况的代码并复用了
						cur = Rmin;
						curParent = RminParent;
						//以下所有代码都是拷贝的第一种情况的代码
						//对应文中的1.1 —— 如果删除的节点是红色叶子节点
						if (cur->_col == red)
						{
							if (cur == curParent->_left)
								curParent->_left = nullptr;//删除cur前,记得断开curParent与cur的连接关系
							else
								curParent->_right = nullptr;//删除cur前,记得断开curParent与cur的连接关系
							delete cur;
							return true;
						}
						//对应文中的1.2 —— 如果删除的节点是黑色,且没有孩子节点,则删除后需要调整平衡
						else
						{
							//情形1:要删除的目标节点cur是根节点,所以删除后无需调整平衡
							if (cur == _root)
							{
								delete cur;
								_root = nullptr;
								return true;
							}

							// 情形2:被删除节点不是根节点,需要进行调整平衡,注意,在向上调整时,不管cur这个黑色节点的是否存在孩子节点,我们都把cur当作没有孩子节点
							int flag = 0;//用于在向上迭代调整的时候,控制不要把cur删除
							while (cur != _root)
							{
								//情形2.2:如果cur是curParent的右孩子
								if (cur == curParent->_right)
								{
									Node* curBrother = curParent->_left;//无需判空,因为cur不是根节点,所以curParent一定不为空
									//情形2.2.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewL->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
										else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curBrother);
											RotateR(curParent);
											curNephewR->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.2.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
									//情形2.2.2:如果cur的兄弟curBrother是红色节点,则说明curBrother一定有左右孩子节点,并且都是黑色
									else
									{
										//当前情况cur是curParent的右孩子,curBrother是curParent的左孩子,所以直接对curParent右单旋
										RotateR(curParent);
										swap(curBrother->_col, curParent->_col);
										//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
										curBrother = curParent->_left;
										//走到这里,情况会转换成2.2.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.2.1的代码

										//情形2.2.1:如果cur的兄弟是黑色节点
										if (curBrother->_col == black)
										{
											//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
											//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
											if (curBrother->_left != nullptr && curBrother->_left->_col == red)
											{
												/*
												下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
												方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
												*/
												Node* curNephewL = curBrother->_left;
												RotateR(curParent);
												curBrother->_col = curParent->_col;
												curParent->_col = black;
												curNephewL->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
											else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
											{
												/*
												下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
												方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
												*/
												Node* curNephewR = curBrother->_right;
												RotateL(curBrother);
												RotateR(curParent);
												curNephewR->_col = curParent->_col;
												curParent->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
											//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
											else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
												|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
											{
												//情形2.2.1.4.1:如果父亲节点是红色
												if (curParent->_col == red)
												{
													curParent->_col = black;
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
													}
													return true;
												}
												//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
												else
												{
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
													}
													flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
													cur = curParent;
													curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
												}
											}
										}
									}
								}
								//情形2.1:如果cur是curParent的左孩子
								else
								{
									Node* curBrother = curParent->_right;//无需判空,因为cur不是根节点,所以curParent一定不为空
									//情形2.1.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
										if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewR->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curBrother);
											RotateL(curParent);
											curNephewL->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.4:如果cur的兄弟没有子节点走这里
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.1.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
									//情形2.1.2:如果cur的兄弟是红色节点
									else
									{
										//当前情况cur是curParent的左孩子,所以curBrother一定是curParent的右孩子,所以直接对curParent左单旋
										RotateL(curParent);
										swap(curBrother->_col, curParent->_col);
										//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
										curBrother = curParent->_right;
										//走到这里,情况会转换成2.1.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.1.1的代码

										//情形2.1.1:如果cur的兄弟是黑色节点
										if (curBrother->_col == black)
										{
											//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
											//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
											if (curBrother->_right != nullptr && curBrother->_right->_col == red)
											{
												/*
												下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
												方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
												*/
												Node* curNephewR = curBrother->_right;
												RotateL(curParent);
												curBrother->_col = curParent->_col;
												curParent->_col = black;
												curNephewR->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
											else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
											{
												/*
												下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
												方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
												*/
												Node* curNephewL = curBrother->_left;
												RotateR(curBrother);
												RotateL(curParent);
												curNephewL->_col = curParent->_col;
												curParent->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.4:如果cur的兄弟没有子节点走这里
											//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
											else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
												|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
											{
												//情形2.1.1.4.1:如果父亲节点是红色
												if (curParent->_col == red)
												{
													curParent->_col = black;
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
													}
													return true;
												}
												//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
												else
												{
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
													}
													flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
													cur = curParent;
													curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
												}
											}
										}
									}
								}
							}
							//走到这里,即走出了用于向上调整的循环,说明是向上调整到了根节点,此时直接return true即可
							return true;
						}
					}
				}
			}
		}
		//没有在树中找到目标节点,说明目标节点不存在,直接return false
		cout << "你要删除的节点不存在" << endl;
		return false;
	}

	pair<iterator,bool> Insert(const pair<Key, Value>& x)//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
	{
		if (_root == nullptr)
		{
			_root = new Node(x, black);
			//return true;
			return make_pair(iterator(_root), true);
		}
		else
		{
			Node* cur = _root;
			Node* curParent = nullptr;
			while (cur != nullptr)
			{
				//if (x > cur->_data)
				if (x.first > cur->_data.first)
				{
					curParent = cur;
					cur = cur->_right;
				}
				//else if (x < cur->_data)
				else if (x.first < cur->_data.first)
				{
					curParent = cur;
					cur = cur->_left;
				}
				else
				{
					//return false;
					return make_pair(iterator(cur), false);
				}
			}
			//找到了nullptr空位置,开始插入
			
			//if (x > curParent->_data)
			if (x.first > curParent->_data.first)
			{
				cur = new Node(x, red);
				curParent->_right = cur;
				cur->_parent = curParent;
			}
			else
			{
				cur = new Node(x, red);
				curParent->_left = cur;
				cur->_parent = curParent;
			}
			//插入新节点成功,开始控制平衡

			Node* temp = cur;//因为下面发生变色调整或者旋转后,cur指针可能会继续往上走,继续进行调整,从而可能让cur指针不再指向新插入的节点,但在调整完毕后return返回值时,是需要返回指向新插入节点的迭代器的,所以这里设置一个temp指针记录新插入节点的地址,防止找不到新插入的节点
			Node* parent = nullptr;
			Node* grandParent = nullptr;
			while (cur->_col == red)//如果调整到根节点来了,就不能再调整了。因为根节点的颜色必须是黑色,所以这里拿cur的颜色是红色作为继续循环的条件
			{
				parent = cur->_parent;//父亲节点不可能为nullptr,因此下一行代码无需判空
				grandParent = parent->_parent;//注意grandParent可能为nullptr

				//如果cur的爷爷节点存在,并且cur和parent是连续红色节点,此时才需要变色控制平衡;否则不需要控制平衡,此时插入新节点成功,直接就可以结束Insert函数了
				if (grandParent != nullptr && (cur->_col == red && parent->_col == red))
				{
					//当cur与parent节点都在grandParent的左子树上时走这里
					if (parent == grandParent->_left)
					{
						Node* uncle = grandParent->_right;

						//第一种情况,此时无所谓cur与parent和grandParent是否为一条直线,但要求parent为红,uncle存在且为红
						//		g
						//	  p	  u
						//  c
						if (parent->_col == red && uncle != nullptr && uncle->_col == red)
						{
							parent->_col = black;
							uncle->_col = black;
							if (grandParent != _root)
								grandParent->_col = red;
							cur = grandParent;
						}
						else if ((parent->_col == red && uncle == nullptr) || (parent->_col == red && uncle != nullptr && uncle->_col == black))
						{
							//第二种情况,此时cur与parent和grandParent是一条直线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  p	  u  
							//  c			
							if (cur == parent->_left)
							{
								RotateR(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp),true);
							}

							//第三种情况,此时cur与parent和grandParent是一条折线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  p	  u  
							//     c		
							else if (cur == parent->_right)
							{
								//先对以p为根节点的树进行左单旋,旋转完毕后将parent指针和cur指针的指向交换,此时节点之间就形成了和第二种情况完全一致的形状,接下来就照着第二种情况的做法去做即可控制平衡。

								//先左单旋
								RotateL(parent);
								swap(parent, cur);//注意一定要交换两个指针的指向,否则本行下面第4行和第5行的代码(即变色的代码)会出问题,会给节点变错颜色。

								//然后按照第二种情况的做法去做
								RotateR(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
						}
					}
					//当cur与parent节点都在grandParent的右子树上时走这里
					else if (parent == grandParent->_right)
					{
						Node* uncle = grandParent->_left;

						//第一种情况,此时无所谓cur与parent和grandParent是否为一条直线,但要求parent为红,uncle存在且为红
						//		g				
						//	  u	  p     	
						//          c
						if (parent->_col == red && uncle != nullptr && uncle->_col == red)
						{
							parent->_col = black;
							uncle->_col = black;
							if (grandParent != _root)
								grandParent->_col = red;
							cur = grandParent;
						}

						else if ((parent->_col == red && uncle == nullptr) || (parent->_col == red && uncle != nullptr && uncle->_col == black))
						{
							//第二种情况,此时cur与parent和grandParent是一条直线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  u	  p  
							//          c
							if (cur == parent->_right)
							{
								RotateL(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
							//第三种情况,此时cur与parent和grandParent是一条折线,parent为红,uncle不存在或者存在且为黑
							//	    g	
							//	  u   p  
							//       c		
							else if (cur == parent->_left)
							{
								//先对以p为根节点的树进行右单旋,旋转完毕后将parent指针和cur指针的指向交换,此时节点之间就形成了和第二种情况完全一致的形状,接下来就照着第二种情况的做法去做即可控制平衡。

								//先右单旋
								RotateR(parent);
								swap(parent, cur);//注意一定要交换两个指针的指向,否则本行下面第4行和第5行的代码(即变色的代码)会出问题,会给节点变错颜色。

								//然后按照第二种情况的做法去做
								RotateL(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
						}
					}
				}
				//如果cur的爷爷节点不存在,或者爷爷节点存在但cur和parent节点不同时为红色,此时不需要变色控制平衡,插入函数直接就可以结束了
				else//(grandParent == nullptr||(grandParent != nullptr && (cur->_col == black || parent->_col == black)))
				{
					//return true;
					return make_pair(iterator(temp), true);
				}
			}
			//如果cur指针一直向上调整到了根节点,根节点被调整完毕后就会走出循环,出了循环后也要return true,不要忘记了
		    //return true;
			return make_pair(iterator(temp), true);
		}
	}


private:
	void RotateR(Node* p)
	{
		if (p != _root)
		{
			Node* subL = p->_left;
			Node* subLR = subL->_right;
			Node* p_parent = p->_parent;

			subL->_right = p;
			subL->_parent = p_parent;

			if (p == p_parent->_right)
				p_parent->_right = subL;
			else
				p_parent->_left = subL;

			p->_left = subLR;
			p->_parent = subL;

			if (subLR != nullptr)
				subLR->_parent = p;
		}
		else
		{
			Node* subL = p->_left;
			Node* subLR = subL->_right;

			_root = subL;
			subL->_right = p;
			subL->_parent = nullptr;

			p->_left = subLR;
			p->_parent = subL;

			if (subLR != nullptr)
				subLR->_parent = p;
		}
	}

	void RotateL(Node* p)
	{
		if (_root != p)
		{
			Node* subR = p->_right;
			Node* subRL = subR->_left;
			Node* p_parent = p->_parent;

			subR->_left = p;
			subR->_parent = p_parent;

			p->_parent = subR;
			p->_right = subRL;

			if (p == p_parent->_right)
				p_parent->_right = subR;
			else
				p_parent->_left = subR;

			if (subRL != nullptr)
				subRL->_parent = p;
		}
		else
		{
			Node* subR = p->_right;
			Node* subRL = subR->_left;

			_root = subR;
			subR->_left = p;
			subR->_parent = nullptr;

			p->_right = subRL;
			p->_parent = subR;

			if (subRL != nullptr)
				subRL->_parent = p;
		}
	}

public:

	bool isbalance()
	{
		//检验性质2是否被破坏
		if (_root != nullptr && _root->_col == red)
		{
			cout << "根节点不是黑色" << endl;
			return false;
		}
		//检验性质3和4是否被破坏,这俩性质都通过子函数_isbalance检验
		else
		{
			int benchmark = 0;
			Node* cur = _root;
			while (cur != nullptr)
			{
				if (cur->_col == black)
				{
					benchmark++;
				}
				cur = cur->_left;
			}
			return _isbalance(_root, 0, benchmark);
		}
	}
private:
	bool _isbalance(Node* root, int blackNum, int benchmark)
	{

		//检测性质3
		if (root == nullptr)
		{
			if (blackNum != benchmark)
			{
				cout << "某条路径上的黑色节点的数量和基准值benchmark不相等,性质3被破坏了" << endl;
				return false;
			}
			else
				return true;
		}

		if (root->_col == black)
			blackNum++;

		//检测性质4
		if (root->_col == red && root->_parent->_col == red)//这里root->_parent不可能为nullptr,因为root的颜色是红色,所以root不可能为整棵树的根节点,所以root->_parent不可能为nullptr。
		{
			cout << "存在连续红色节点,性质4被破坏" << endl;
			return false;
		}

		bool x = _isbalance(root->_left, blackNum, benchmark);
		bool y = _isbalance(root->_right, blackNum, benchmark);
		return x && y;
	}


public:
	void Inorder()
	{
		_Inorder(_root);
	}
private:
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_data << ' ';
		_Inorder(root->_right);
	}
private:
	Node* _root;
};

map.h的代码如下。

#pragma once
#include"RBTree.h";



template<class Key,class T>//这里的T就是value,写成T是为了和STL标准的一样
class Map
{
public:
	//文中说过,map就等价于红黑树,所以红黑树的迭代器就是map的迭代器
	typedef RBTreeIterator<pair<Key, T>, pair<Key, T>> iterator;
	typedef RBTreeIterator<pair<Key, T>, const pair<Key, T>> const_iterator;
	Map()
		:_rbt()
	{}

	iterator begin()
	{
		return _rbt.begin();
	}
	
	const_iterator begin()const
	{
		return _rbt.begin();
	}

	iterator end()
	{
		return _rbt.end();
	}

	const_iterator end()const
	{
		return _rbt.end();
	}

	pair<iterator ,bool> Insert(const pair<Key, T>& p)
	{
		return _rbt.Insert(p);
	}

	void Erase(const Key& k)
	{
		_rbt.Erase(k);
	}

	T& operator[](const Key& k)
	{
		pair<iterator, bool> temp = _rbt.Insert(make_pair(k, T()));
		return (*(temp.first)).second;
	}
private:
	RBTree<Key,T> _rbt;
};

test.cpp的代码如下。

#include"Set.h";
#include"Map.h";

void test1()
{
	RBTree<int, int>r;
	r.Insert(make_pair(1, 1));
	r.Insert(make_pair(4, 4));
	r.Insert(make_pair(2, 2));
	r.Insert(make_pair(3, 3));
	const RBTree<int, int>r1(r);

	for (RBTree<int, int>::const_iterator it = r1.begin(); it != r1.end(); it++)
	{
		cout << (*it).first << ' ';
	}
	cout << endl;
}

void test2()
{
	string s[] = { "苹果","香蕉","牛奶","香蕉","橙子","苹果","菠萝"};
	Map<string, int>m;
	
	//对map的插入的检测如下
	for (string& e : s)
	{
		m[e]++;//因为map的operator[]是对map的Insert(或者说是对RBTree的Insert)的一层封装,所以如果检测时发现operator[]没有问题,那map的Insert也肯定是没问题的
	}

	//对map的迭代器部分、map的begin()、end()的检测如下
	for (Map<string, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << it->first << ":" << it->second<<endl;
	}
	cout << endl;

	//对map的删除的检测如下
	m.Erase("香蕉");
	m.Erase("牛奶");
	m.Erase("菠萝");

	//支持map的迭代器后就支持范围for了
	for (pair<string, int>& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;

}
void main()
{
	test2();
}



set的模拟实现 

对于set的模拟实现,它的很多细节和map是完全相同的,比如set和map一样,他俩都等价于红黑树,只不过树上挂的节点是一个个pair。既然set也等价于红黑树,所以set的迭代器就是RBTree的迭代器,直接拷贝RBTree的迭代器的代码即可,不需要作任何修改,并且因为set等价于红黑树,所以set的begin()等接口就等价于RBTree的begin()等接口,所以实现set的begin()等接口就是对RBTree的begin()等接口套一层封装。

set和map在模拟实现时的区别非常少,只有1:set的Insert需要的参数不是pair(但注意最后通过红黑树的Insert时,插入的节点的有效数据的类型还是pair),2:set不像map,set没有operator[]这个成员函数。

set剩下的细节几乎完全和map一致,这里就不再赘述,而是直接上整体的代码。

set的整体代码

RBTree.h的代码如下。

再次说明,下面RBTree.h的代码是专门提供给Map和Set的底层的,和<<红黑树(RBTree)的模拟实现>>一文中的正常的RBTree.h的代码不完全一样,是经过了稍微的修改的,至于修改了哪些内容,在上文中已经全部说过了。


/*
	注意这个版本的RBTree.h是提供给Map和Set的底层的,和正常的RBTree不一样
*/


#pragma once
#include<iostream>
using namespace std;

enum color
{
	red,
	black
};

template<class T>
class RBTreeNode
{
public:
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	color _col;

	RBTreeNode()
		:_data()
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col()
	{}

	RBTreeNode(const T& x, color col)
		:_data(x)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(col)
	{}
};

template<class T1,class T2>
struct RBTreeIterator
{
	typedef RBTreeNode<T1> Node;
	RBTreeIterator()
		:_n(nullptr)
	{}
	RBTreeIterator(Node* n)
		:_n(n)
	{}

	T2& operator*()
	{
		return (*_n)._data;
	}

	T2* operator->()
	{
		return &((*_n)._data);
	}

	bool operator==(const RBTreeIterator it)const //加const是为了让被const iterator也能调用该接口,注意区分const iterator和const_iterator,前者不是const迭代器
	{
		return _n == it._n;
	}

	bool operator!=(const RBTreeIterator it)const //加const是为了让被const iterator也能调用该接口,注意区分const iterator和const_iterator,前者不是const迭代器
	{
		return !(operator==(it));
	}


	RBTreeIterator operator++()//前置++
	{
		if (_n->_right == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_left)
				{
					_n = curParent;
					return (*this);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return (*this);
		}
		else
		{
			Node* Rmin = _n->_right;
			while (Rmin->_left != nullptr)
			{
				Rmin = Rmin->_left;
			}
			_n = Rmin;
			return (*this);
		}
	}

	RBTreeIterator operator++(int)//后置++
	{
		Node* temp = _n;
		if (_n->_right == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_left)
				{
					_n = curParent;
					return RBTreeIterator(temp);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return
			_n = nullptr;
			return RBTreeIterator(temp);
		}
		else
		{
			Node* Rmin = _n->_right;
			while (Rmin->_left != nullptr)
			{
				Rmin = Rmin->_left;
			}
			_n = Rmin;
			return RBTreeIterator(temp);
		}
	}

	RBTreeIterator operator--()//前置--
	{
		if (_n->_left == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_right)
				{
					_n = curParent;
					return (*this);//this指针的类型是RBTreeIterator<T>*, this指针的值是调用operator++接口的迭代器对象的地址
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return (*this);
		}
		else
		{
			Node* Lmax = _n->_left;
			while (Lmax->_right != nullptr)
			{
				Lmax = Lmax->_right;
			}
			_n = Lmax;
			return (*this);
		}
	}

	RBTreeIterator operator--(int)//后置--
	{
		Node* temp = _n;
		if (_n->_left == nullptr)
		{
			Node* cur = _n;
			Node* curParent = _n->_parent;
			while (curParent != nullptr)//这里本来是cur!=_root的,但_root在RBTree类中,访问不到,所以用了curParent!=nullptr替代
			{
				if (cur == curParent->_right)
				{
					_n = curParent;
					return RBTreeIterator(temp);
				}
				else
				{
					cur = curParent;
					curParent = curParent->_parent;
				}
			}
			//如果出了while循环,说明cur走到了根节点,此时说明整棵树就已经遍历完毕了,直接return 一个【和end()函数返回的迭代器指向相同的】迭代器
			_n = nullptr;
			return RBTreeIterator(temp);
		}
		else
		{
			Node* Lmax = _n->_left;
			while (Lmax->_right != nullptr)
			{
				Lmax = Lmax->_right;
			}
			_n = Lmax;
			return RBTreeIterator(temp);
		}
	}
	
	Node* _n;
};

template<class Key,class Value>//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
class RBTree
{
	typedef RBTreeNode<pair<Key,Value>> Node;
public:
	typedef RBTreeIterator<pair<Key,Value>, pair<Key, Value>> iterator;
	typedef RBTreeIterator<pair<Key, Value>,const pair<Key, Value>> const_iterator;

	RBTree()
		:_root(nullptr)
	{}

	iterator begin()
	{
		if (_root == nullptr)
			return iterator(nullptr);
		else
		{
			Node* cur = _root;
			while (cur->_left != nullptr)
				cur = cur->_left;
			return iterator(cur);
		}
	}

	const_iterator begin()const
	{
		if (_root == nullptr)
			return const_iterator(nullptr);
		else
		{
			Node* cur = _root;
			while (cur->_left != nullptr)
				cur = cur->_left;
			return const_iterator(cur);
		}
	}

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

	const_iterator end()const
	{
		return const_iterator(nullptr);
	}


	bool Erase(const Key& k)//RBTree作为map或者set的底层容器时,T一定是pair类模板实例化出的类
	{
		if (_root == nullptr)
		{
			cout << "树中已经不存在节点了,无法继续删除" << endl;
			return false;
		}
		Node* cur = _root;
		Node* curParent = cur->_parent;
		while (cur != nullptr)
		{
			//if (x < cur->_data)			
			if (k < cur->_data.first)
			{
				curParent = cur;
				cur = cur->_left;
			}
			//else if (x > cur->_data)
			else if (k > cur->_data.first)
			{
				curParent = cur;
				cur = cur->_right;
			}
			//找到了要删除的目标节点,开始删除
			else
			{
				//第一种情况,如果目标节点没有孩子节点
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					//对应文中的1.1 —— 如果删除的节点是红色叶子节点
					if (cur->_col == red)
					{
						if (cur == curParent->_left)
							curParent->_left = nullptr;//删除cur前,记得断开curParent与cur的连接关系
						else
							curParent->_right = nullptr;//删除cur前,记得断开curParent与cur的连接关系
						delete cur;
						return true;
					}
					//对应文中的1.2 —— 如果删除的节点是黑色,且没有孩子节点,则删除后需要调整平衡
					else
					{
						//情形1:要删除的目标节点cur是根节点,所以删除后无需调整平衡
						if (cur == _root)
						{
							delete cur;
							_root = nullptr;
							return true;
						}

						// 情形2:被删除节点不是根节点,需要进行调整平衡,注意,在向上调整时,不管cur这个黑色节点的是否存在孩子节点,我们都把cur当作没有孩子节点
						int flag = 0;//用于在向上迭代调整的时候,控制不要把cur删除
						while (cur != _root)
						{
							//情形2.2:如果cur是curParent的右孩子
							if (cur == curParent->_right)
							{
								Node* curBrother = curParent->_left;//无需判空,因为cur不是根节点,所以curParent一定不为空
								//情形2.2.1:如果cur的兄弟是黑色节点
								if (curBrother->_col == black)
								{
									//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
									//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
									if (curBrother->_left != nullptr && curBrother->_left->_col == red)
									{
										/*
										下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
										方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
										*/
										Node* curNephewL = curBrother->_left;
										RotateR(curParent);
										curBrother->_col = curParent->_col;
										curParent->_col = black;
										curNephewL->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
									else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
									{
										/*
										下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
										方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
										*/
										Node* curNephewR = curBrother->_right;
										RotateL(curBrother);
										RotateR(curParent);
										curNephewR->_col = curParent->_col;
										curParent->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
									//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
									else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
										|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
									{
										//情形2.2.1.4.1:如果父亲节点是红色
										if (curParent->_col == red)
										{
											curParent->_col = black;
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
										else
										{
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
											cur = curParent;
											curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
										}
									}
								}
								//情形2.2.2:如果cur的兄弟curBrother是红色节点,则说明curBrother一定有左右孩子节点,并且都是黑色
								else
								{
									//当前情况cur是curParent的右孩子,curBrother是curParent的左孩子,所以直接对curParent右单旋
									RotateR(curParent);
									swap(curBrother->_col, curParent->_col);
									//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
									curBrother = curParent->_left;
									//走到这里,情况会转换成2.2.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.2.1的代码

									//情形2.2.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewL->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
										else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curBrother);
											RotateR(curParent);
											curNephewR->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.2.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
								}
							}
							//情形2.1:如果cur是curParent的左孩子
							else
							{
								Node* curBrother = curParent->_right;//无需判空,因为cur不是根节点,所以curParent一定不为空
								//情形2.1.1:如果cur的兄弟是黑色节点
								if (curBrother->_col == black)
								{
									//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
									//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
									if (curBrother->_right != nullptr && curBrother->_right->_col == red)
									{
										/*
										下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
										方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
										*/
										Node* curNephewR = curBrother->_right;
										RotateL(curParent);
										curBrother->_col = curParent->_col;
										curParent->_col = black;
										curNephewR->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
									//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
									else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
									{
										/*
										下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
										个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
										在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
										方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
										*/
										Node* curNephewL = curBrother->_left;
										RotateR(curBrother);
										RotateL(curParent);
										curNephewL->_col = curParent->_col;
										curParent->_col = black;
										if (flag == 0)
										{
											delete cur;
											curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
										}
										return true;
									}
									//情形2.1.1.4:如果cur的兄弟没有子节点走这里
									//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
									else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
										|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
									{
										//情形2.1.1.4.1:如果父亲节点是红色
										if (curParent->_col == red)
										{
											curParent->_col = black;
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
										else
										{
											curBrother->_col = red;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
											cur = curParent;
											curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
										}
									}
								}
								//情形2.1.2:如果cur的兄弟是红色节点
								else
								{
									//当前情况cur是curParent的左孩子,所以curBrother一定是curParent的右孩子,所以直接对curParent左单旋
									RotateL(curParent);
									swap(curBrother->_col, curParent->_col);
									//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
									curBrother = curParent->_right;
									//走到这里,情况会转换成2.1.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.1.1的代码

									//情形2.1.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
										if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewR->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curBrother);
											RotateL(curParent);
											curNephewL->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.4:如果cur的兄弟没有子节点走这里
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.1.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
								}
							}
						}
						//走到这里,即走出了用于向上调整的循环,说明是向上调整到了根节点,此时直接return true即可
						return true;
					}
				}
				//第二种情况,如果目标节点只有左孩子
				else if (cur->_left != nullptr && cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
						_root->_parent = nullptr;//不要忘了修改新根节点的parent指针的指向
						_root->_col = black;//如果要删除的目标节点是整棵树的根节点,并且还只有左孩子,则说明左孩子一定为红,此时一定不要忘了的修改新根节点的颜色,把他从红色变成黑色
						delete cur;
						return true;
					}
					else
					{
						if (cur == curParent->_left)
						{
							curParent->_left = cur->_left;
							cur->_left->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						else
						{
							curParent->_right = cur->_left;
							cur->_left->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						cur->_left->_col = black;
						delete cur;
						return true;
					}
				}
				//第三种情况,如果目标节点只有右孩子
				else if (cur->_left == nullptr && cur->_right != nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
						_root->_parent = nullptr;//不要忘了修改新根节点的parent指针的指向
						_root->_col = black;//如果要删除的目标节点是整棵树的根节点,并且还只有右孩子,则说明右孩子一定为红,此时一定不要忘了的修改新根节点的颜色,把他从红色变成黑色
						delete cur;
						return true;
					}
					else
					{
						if (cur == curParent->_left)
						{
							curParent->_left = cur->_right;
							cur->_right->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						else
						{
							curParent->_right = cur->_right;
							cur->_right->_parent = curParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						cur->_right->_col = black;
						delete cur;
						return true;
					}
				}
				//第四种情况,如果目标节点既有左孩子,又有右孩子
				else if (cur->_left != nullptr && cur->_right != nullptr)
				{
					Node* Rmin = cur->_right;
					Node* RminParent = cur;
					while (Rmin->_left != nullptr)
					{
						RminParent = Rmin;
						Rmin = Rmin->_left;
					}
					cur->_data = Rmin->_data;
					//走到这里,替死鬼节点Rmin就被找到了,Rmin只有两种可能,第一:只有右孩子,第二:没有孩子节点

					//如果只有右孩子,则套用上面的第三种情况的思路即可。
					if (Rmin->_right != nullptr)
					{
						//注意Rmin不可能是整棵树的根节点,所以不必一板一眼的套用第三种情况的思路
						if (Rmin == RminParent->_left)
						{
							RminParent->_left = Rmin->_right;
							Rmin->_right->_parent = RminParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						else
						{
							RminParent->_right = Rmin->_right;
							Rmin->_right->_parent = RminParent;//别忘了更新每个节点的_parent指针,这点坑死我了,调试死我了
						}
						Rmin->_right->_col = black;
						delete Rmin;
						return true;
					}
					//替死鬼节点Rmin没有孩子节点,此时直接套用第一种情况的思路即可,但注意,因为第一种情况的代码量太大了,这里我们就不套用思路了,而是直接拷贝第一种情况的代码
					else
					{
						//先把Rmin和RminParent指针的值赋给cur与curParent,这样我们就可以直接拷贝第一种情况的代码并复用了
						cur = Rmin;
						curParent = RminParent;
						//以下所有代码都是拷贝的第一种情况的代码
						//对应文中的1.1 —— 如果删除的节点是红色叶子节点
						if (cur->_col == red)
						{
							if (cur == curParent->_left)
								curParent->_left = nullptr;//删除cur前,记得断开curParent与cur的连接关系
							else
								curParent->_right = nullptr;//删除cur前,记得断开curParent与cur的连接关系
							delete cur;
							return true;
						}
						//对应文中的1.2 —— 如果删除的节点是黑色,且没有孩子节点,则删除后需要调整平衡
						else
						{
							//情形1:要删除的目标节点cur是根节点,所以删除后无需调整平衡
							if (cur == _root)
							{
								delete cur;
								_root = nullptr;
								return true;
							}

							// 情形2:被删除节点不是根节点,需要进行调整平衡,注意,在向上调整时,不管cur这个黑色节点的是否存在孩子节点,我们都把cur当作没有孩子节点
							int flag = 0;//用于在向上迭代调整的时候,控制不要把cur删除
							while (cur != _root)
							{
								//情形2.2:如果cur是curParent的右孩子
								if (cur == curParent->_right)
								{
									Node* curBrother = curParent->_left;//无需判空,因为cur不是根节点,所以curParent一定不为空
									//情形2.2.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewL->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
										else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curBrother);
											RotateR(curParent);
											curNephewR->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.2.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
									//情形2.2.2:如果cur的兄弟curBrother是红色节点,则说明curBrother一定有左右孩子节点,并且都是黑色
									else
									{
										//当前情况cur是curParent的右孩子,curBrother是curParent的左孩子,所以直接对curParent右单旋
										RotateR(curParent);
										swap(curBrother->_col, curParent->_col);
										//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
										curBrother = curParent->_left;
										//走到这里,情况会转换成2.2.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.2.1的代码

										//情形2.2.1:如果cur的兄弟是黑色节点
										if (curBrother->_col == black)
										{
											//情形2.2.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
											//情形2.2.1.3:说明一下,如果兄弟有两个红色子节点,也走这里,因为这个分支不用双旋,更好
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
											if (curBrother->_left != nullptr && curBrother->_left->_col == red)
											{
												/*
												下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
												方便在对curParent节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
												*/
												Node* curNephewL = curBrother->_left;
												RotateR(curParent);
												curBrother->_col = curParent->_col;
												curParent->_col = black;
												curNephewL->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支									
											else if (curBrother->_right != nullptr && curBrother->_right->_col == red)
											{
												/*
												下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
												方便在对curBrother节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
												*/
												Node* curNephewR = curBrother->_right;
												RotateL(curBrother);
												RotateR(curParent);
												curNephewR->_col = curParent->_col;
												curParent->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.2.1.4:如果cur的兄弟没有子节点走这里  
											//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
											else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
												|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
											{
												//情形2.2.1.4.1:如果父亲节点是红色
												if (curParent->_col == red)
												{
													curParent->_col = black;
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
													}
													return true;
												}
												//情形2.2.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
												else
												{
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_right = nullptr;//cur是curParent的右孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur不需要被删除,但连接被断开的情况
													}
													flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把他删除
													cur = curParent;
													curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
												}
											}
										}
									}
								}
								//情形2.1:如果cur是curParent的左孩子
								else
								{
									Node* curBrother = curParent->_right;//无需判空,因为cur不是根节点,所以curParent一定不为空
									//情形2.1.1:如果cur的兄弟是黑色节点
									if (curBrother->_col == black)
									{
										//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
										//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
										if (curBrother->_right != nullptr && curBrother->_right->_col == red)
										{
											/*
											下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
											方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
											*/
											Node* curNephewR = curBrother->_right;
											RotateL(curParent);
											curBrother->_col = curParent->_col;
											curParent->_col = black;
											curNephewR->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
										//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
										else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
										{
											/*
											下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
											个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
											在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
											方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
											*/
											Node* curNephewL = curBrother->_left;
											RotateR(curBrother);
											RotateL(curParent);
											curNephewL->_col = curParent->_col;
											curParent->_col = black;
											if (flag == 0)
											{
												delete cur;
												curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
											}
											return true;
										}
										//情形2.1.1.4:如果cur的兄弟没有子节点走这里
										//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
										else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
											|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
										{
											//情形2.1.1.4.1:如果父亲节点是红色
											if (curParent->_col == red)
											{
												curParent->_col = black;
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
											else
											{
												curBrother->_col = red;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
												cur = curParent;
												curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
											}
										}
									}
									//情形2.1.2:如果cur的兄弟是红色节点
									else
									{
										//当前情况cur是curParent的左孩子,所以curBrother一定是curParent的右孩子,所以直接对curParent左单旋
										RotateL(curParent);
										swap(curBrother->_col, curParent->_col);
										//旋转完毕后,关系可能会乱,所以更新curBrother与curParent指针的指向,这里我们根据示例图可以知道只有curBrother指针需要更新
										curBrother = curParent->_right;
										//走到这里,情况会转换成2.1.1:cur和curBrother都是黑色节点,此时直接拷贝上面2.1.1的代码

										//情形2.1.1:如果cur的兄弟是黑色节点
										if (curBrother->_col == black)
										{
											//情形2.1.1.2:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色右子节点。
											//情形2.1.1.3:如果兄弟有两个红色子节点,我们也走这里,因为这个分支不用双旋,更好
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为黑,右孩子为红。因为文中说过我们要把它当作左孩子为空,右孩子为红,所以才需要走这个if分支
											if (curBrother->_right != nullptr && curBrother->_right->_col == red)
											{
												/*
												下面的curNephewR表示cur的侄子节点,有个R说明是curBrother的右孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewR节点的地址,
												方便在对curParent节点旋转后还能找到curBrother的右孩子节点,为什么找它呢?需要对curBrother的右孩子节点变色调整。
												*/
												Node* curNephewR = curBrother->_right;
												RotateL(curParent);
												curBrother->_col = curParent->_col;
												curParent->_col = black;
												curNephewR->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.1:如果cur的兄弟有子节点,则只有可能为红色,当前情况就是兄弟只有红色左子节点。
											//说明一下,还有一种只存在于向上调整时的特殊情况也要走这里,就是curBrother的左孩子为红,右孩子为黑。因为文中说过我们要把它当作左孩子为红,右孩子为空,所以才需要走这个if分支
											else if (curBrother->_left != nullptr && curBrother->_left->_col == red)
											{
												/*
												下面的curNephewL表示cur的侄子节点,有个L说明是curBrother的左孩子;为什么要设置该指针呢?因为这里有一
												个巨坑,因为对某个节点旋转,可能会修改该节点的结构,比如导致儿子节点不再是儿子节点,关系会乱,所以如果
												在发生旋转后还通过该节点来找它的孩子或者父亲节点,就不一定能找到,所以这里提前记录了curNephewL节点的地址,
												方便在对curBrother节点旋转后还能找到curBrother的左孩子节点,为什么找它呢?需要对curBrother的左孩子节点变色调整。
												*/
												Node* curNephewL = curBrother->_left;
												RotateR(curBrother);
												RotateL(curParent);
												curNephewL->_col = curParent->_col;
												curParent->_col = black;
												if (flag == 0)
												{
													delete cur;
													curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
												}
												return true;
											}
											//情形2.1.1.4:如果cur的兄弟没有子节点走这里
											//说明一下,还有一种只存在于向上调整时的特殊情况也走这里,就是cur的兄弟存在两个子节点,但子节点必须全是黑色节点,不能有红色存在
											else if ((curBrother->_left == nullptr && curBrother->_right == nullptr)
												|| ((curBrother->_left != nullptr && curBrother->_left->_col == black) && (curBrother->_right != nullptr && curBrother->_right->_col == black)))
											{
												//情形2.1.1.4.1:如果父亲节点是红色
												if (curParent->_col == red)
												{
													curParent->_col = black;
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
													}
													return true;
												}
												//情形2.1.1.4.2:如果父亲节点是黑色,则需要向上迭代调整
												else
												{
													curBrother->_col = red;
													if (flag == 0)
													{
														delete cur;
														curParent->_left = nullptr;//cur是curParent的左孩子,删除cur后,记得把链接关系断开,写在if语句里就是为了防止向上调整的时候出现cur没有被删除,但连接被断开的情况
													}
													flag = 1;//用于在向上迭代调整的时候,控制不要把cur删除,这里设置成1后,说明已经删除了目标节点,后序cur指向的节点只是在调整颜色,不需要真的把cur删除
													cur = curParent;
													curParent = curParent->_parent;//别忘了把curParent也向上调整,这个坑调试死我了,我屮
												}
											}
										}
									}
								}
							}
							//走到这里,即走出了用于向上调整的循环,说明是向上调整到了根节点,此时直接return true即可
							return true;
						}
					}
				}
			}
		}
		//没有在树中找到目标节点,说明目标节点不存在,直接return false
		cout << "你要删除的节点不存在" << endl;
		return false;
	}

	pair<iterator,bool> Insert(const pair<Key, Value>& x)//RBTree作为map或者set的底层容器时,所存储的元素(节点)一定是pair
	{
		if (_root == nullptr)
		{
			_root = new Node(x, black);
			//return true;
			return make_pair(iterator(_root), true);
		}
		else
		{
			Node* cur = _root;
			Node* curParent = nullptr;
			while (cur != nullptr)
			{
				//if (x > cur->_data)
				if (x.first > cur->_data.first)
				{
					curParent = cur;
					cur = cur->_right;
				}
				//else if (x < cur->_data)
				else if (x.first < cur->_data.first)
				{
					curParent = cur;
					cur = cur->_left;
				}
				else
				{
					//return false;
					return make_pair(iterator(cur), false);
				}
			}
			//找到了nullptr空位置,开始插入
			
			//if (x > curParent->_data)
			if (x.first > curParent->_data.first)
			{
				cur = new Node(x, red);
				curParent->_right = cur;
				cur->_parent = curParent;
			}
			else
			{
				cur = new Node(x, red);
				curParent->_left = cur;
				cur->_parent = curParent;
			}
			//插入新节点成功,开始控制平衡

			Node* temp = cur;//因为下面发生变色调整或者旋转后,cur指针可能会继续往上走,继续进行调整,从而可能让cur指针不再指向新插入的节点,但在调整完毕后return返回值时,是需要返回指向新插入节点的迭代器的,所以这里设置一个temp指针记录新插入节点的地址,防止找不到新插入的节点
			Node* parent = nullptr;
			Node* grandParent = nullptr;
			while (cur->_col == red)//如果调整到根节点来了,就不能再调整了。因为根节点的颜色必须是黑色,所以这里拿cur的颜色是红色作为继续循环的条件
			{
				parent = cur->_parent;//父亲节点不可能为nullptr,因此下一行代码无需判空
				grandParent = parent->_parent;//注意grandParent可能为nullptr

				//如果cur的爷爷节点存在,并且cur和parent是连续红色节点,此时才需要变色控制平衡;否则不需要控制平衡,此时插入新节点成功,直接就可以结束Insert函数了
				if (grandParent != nullptr && (cur->_col == red && parent->_col == red))
				{
					//当cur与parent节点都在grandParent的左子树上时走这里
					if (parent == grandParent->_left)
					{
						Node* uncle = grandParent->_right;

						//第一种情况,此时无所谓cur与parent和grandParent是否为一条直线,但要求parent为红,uncle存在且为红
						//		g
						//	  p	  u
						//  c
						if (parent->_col == red && uncle != nullptr && uncle->_col == red)
						{
							parent->_col = black;
							uncle->_col = black;
							if (grandParent != _root)
								grandParent->_col = red;
							cur = grandParent;
						}
						else if ((parent->_col == red && uncle == nullptr) || (parent->_col == red && uncle != nullptr && uncle->_col == black))
						{
							//第二种情况,此时cur与parent和grandParent是一条直线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  p	  u  
							//  c			
							if (cur == parent->_left)
							{
								RotateR(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp),true);
							}

							//第三种情况,此时cur与parent和grandParent是一条折线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  p	  u  
							//     c		
							else if (cur == parent->_right)
							{
								//先对以p为根节点的树进行左单旋,旋转完毕后将parent指针和cur指针的指向交换,此时节点之间就形成了和第二种情况完全一致的形状,接下来就照着第二种情况的做法去做即可控制平衡。

								//先左单旋
								RotateL(parent);
								swap(parent, cur);//注意一定要交换两个指针的指向,否则本行下面第4行和第5行的代码(即变色的代码)会出问题,会给节点变错颜色。

								//然后按照第二种情况的做法去做
								RotateR(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
						}
					}
					//当cur与parent节点都在grandParent的右子树上时走这里
					else if (parent == grandParent->_right)
					{
						Node* uncle = grandParent->_left;

						//第一种情况,此时无所谓cur与parent和grandParent是否为一条直线,但要求parent为红,uncle存在且为红
						//		g				
						//	  u	  p     	
						//          c
						if (parent->_col == red && uncle != nullptr && uncle->_col == red)
						{
							parent->_col = black;
							uncle->_col = black;
							if (grandParent != _root)
								grandParent->_col = red;
							cur = grandParent;
						}

						else if ((parent->_col == red && uncle == nullptr) || (parent->_col == red && uncle != nullptr && uncle->_col == black))
						{
							//第二种情况,此时cur与parent和grandParent是一条直线,parent为红,uncle不存在或者存在且为黑
							//		g	
							//	  u	  p  
							//          c
							if (cur == parent->_right)
							{
								RotateL(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
							//第三种情况,此时cur与parent和grandParent是一条折线,parent为红,uncle不存在或者存在且为黑
							//	    g	
							//	  u   p  
							//       c		
							else if (cur == parent->_left)
							{
								//先对以p为根节点的树进行右单旋,旋转完毕后将parent指针和cur指针的指向交换,此时节点之间就形成了和第二种情况完全一致的形状,接下来就照着第二种情况的做法去做即可控制平衡。

								//先右单旋
								RotateR(parent);
								swap(parent, cur);//注意一定要交换两个指针的指向,否则本行下面第4行和第5行的代码(即变色的代码)会出问题,会给节点变错颜色。

								//然后按照第二种情况的做法去做
								RotateL(grandParent);
								parent->_col = black;
								grandParent->_col = red;
								//return true;
								return make_pair(iterator(temp), true);
							}
						}
					}
				}
				//如果cur的爷爷节点不存在,或者爷爷节点存在但cur和parent节点不同时为红色,此时不需要变色控制平衡,插入函数直接就可以结束了
				else//(grandParent == nullptr||(grandParent != nullptr && (cur->_col == black || parent->_col == black)))
				{
					//return true;
					return make_pair(iterator(temp), true);
				}
			}
			//如果cur指针一直向上调整到了根节点,根节点被调整完毕后就会走出循环,出了循环后也要return true,不要忘记了
		    //return true;
			return make_pair(iterator(temp), true);
		}
	}


private:
	void RotateR(Node* p)
	{
		if (p != _root)
		{
			Node* subL = p->_left;
			Node* subLR = subL->_right;
			Node* p_parent = p->_parent;

			subL->_right = p;
			subL->_parent = p_parent;

			if (p == p_parent->_right)
				p_parent->_right = subL;
			else
				p_parent->_left = subL;

			p->_left = subLR;
			p->_parent = subL;

			if (subLR != nullptr)
				subLR->_parent = p;
		}
		else
		{
			Node* subL = p->_left;
			Node* subLR = subL->_right;

			_root = subL;
			subL->_right = p;
			subL->_parent = nullptr;

			p->_left = subLR;
			p->_parent = subL;

			if (subLR != nullptr)
				subLR->_parent = p;
		}
	}

	void RotateL(Node* p)
	{
		if (_root != p)
		{
			Node* subR = p->_right;
			Node* subRL = subR->_left;
			Node* p_parent = p->_parent;

			subR->_left = p;
			subR->_parent = p_parent;

			p->_parent = subR;
			p->_right = subRL;

			if (p == p_parent->_right)
				p_parent->_right = subR;
			else
				p_parent->_left = subR;

			if (subRL != nullptr)
				subRL->_parent = p;
		}
		else
		{
			Node* subR = p->_right;
			Node* subRL = subR->_left;

			_root = subR;
			subR->_left = p;
			subR->_parent = nullptr;

			p->_right = subRL;
			p->_parent = subR;

			if (subRL != nullptr)
				subRL->_parent = p;
		}
	}

public:

	bool isbalance()
	{
		//检验性质2是否被破坏
		if (_root != nullptr && _root->_col == red)
		{
			cout << "根节点不是黑色" << endl;
			return false;
		}
		//检验性质3和4是否被破坏,这俩性质都通过子函数_isbalance检验
		else
		{
			int benchmark = 0;
			Node* cur = _root;
			while (cur != nullptr)
			{
				if (cur->_col == black)
				{
					benchmark++;
				}
				cur = cur->_left;
			}
			return _isbalance(_root, 0, benchmark);
		}
	}
private:
	bool _isbalance(Node* root, int blackNum, int benchmark)
	{

		//检测性质3
		if (root == nullptr)
		{
			if (blackNum != benchmark)
			{
				cout << "某条路径上的黑色节点的数量和基准值benchmark不相等,性质3被破坏了" << endl;
				return false;
			}
			else
				return true;
		}

		if (root->_col == black)
			blackNum++;

		//检测性质4
		if (root->_col == red && root->_parent->_col == red)//这里root->_parent不可能为nullptr,因为root的颜色是红色,所以root不可能为整棵树的根节点,所以root->_parent不可能为nullptr。
		{
			cout << "存在连续红色节点,性质4被破坏" << endl;
			return false;
		}

		bool x = _isbalance(root->_left, blackNum, benchmark);
		bool y = _isbalance(root->_right, blackNum, benchmark);
		return x && y;
	}


public:
	void Inorder()
	{
		_Inorder(_root);
	}
private:
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_data << ' ';
		_Inorder(root->_right);
	}
private:
	Node* _root;
};

set.h的代码如下。

这里顺便回顾几个知识点,因为map的Insert和Erase接口都不是const成员函数,所以如果有const set<T> s,s对象是无法调用Insert和Erase函数的,s对象只能调用begin()和end()。然后再顺便一说,非const的类对象也是能够调用const成员函数的,但下面因为存在非const版本的begin()和end()接口,构成函数重载,所以这里非const的set对象调用begin()和end()时,只会调用非const版本的begin()和end()。

#include"RBTree.h";

template<class T>
class Set
{
public:
	typedef RBTreeIterator<pair<T, T>, pair<T, T>> iterator;
	typedef RBTreeIterator<pair<T, T>, const pair<T, T>> const_iterator;
	Set()
		:_rbt()
	{}

	pair<iterator, bool> Insert(const T& x)
	{
		return _rbt.Insert(pair<T, T>(x, x));
	}

	void Erase(const T& x)
	{
		_rbt.Erase(x);
	}

	iterator begin()
	{
		return _rbt.begin();
	}

	const_iterator begin()const
	{
		return _rbt.begin();
	}

	iterator end()
	{
		return _rbt.end();
	}

	const_iterator end()const
	{
		return _rbt.end();
	}

private:
	RBTree<T, T>_rbt;
};

test.cpp的代码如下。

#include"Set.h";
#include"Map.h";

void test1()
{
	RBTree<int, int>r;
	r.Insert(make_pair(1, 1));
	r.Insert(make_pair(4, 4));
	r.Insert(make_pair(2, 2));
	r.Insert(make_pair(3, 3));
	const RBTree<int, int>r1(r);

	for (RBTree<int, int>::const_iterator it = r1.begin(); it != r1.end(); it++)
	{
		cout << (*it).first << ' ';
	}
	cout << endl;
}

void test2()
{
	string s[] = { "苹果","香蕉","牛奶","香蕉","橙子","苹果","菠萝"};
	Map<string, int>m;
	
	//对map的插入的检测如下
	for (string& e : s)
	{
		m[e]++;//因为map的operator[]是对map的Insert(或者说是对RBTree的Insert)的一层封装,所以如果检测时发现operator[]没有问题,那map的Insert也肯定是没问题的
	}

	//对map的迭代器部分、map的begin()、end()的检测如下
	for (Map<string, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << it->first << ":" << it->second<<endl;
	}
	cout << endl;

	//对map的删除的检测如下
	m.Erase("香蕉");
	m.Erase("牛奶");
	m.Erase("菠萝");

	//支持map的迭代器后就支持范围for了
	for (pair<string, int>& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
}

void test3()
{
	Set<string>s;

	//对set的插入的检测如下
	s.Insert("香蕉");
	s.Insert("牛奶");
	s.Insert("菠萝");
	s.Insert("苹果");
	s.Insert("草莓");

	//对set的迭代器类RBTreeIterator的operator系列等接口的检测如下
	//对set的begin()等接口的检测如下
	for (Set<string>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << it->first << ' ';
	}
	cout << endl;

	//对set的删除的检测如下
	s.Erase("牛奶");
	s.Erase("菠萝");
	s.Erase("香蕉");
	for (Set<string>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << it->first << ' ';
	}
	cout << endl;

	//set的迭代器实现后,范围for也就被支持了
	for (pair<string, string>& p : s)
	{
		cout << p.first << ' ';
	}
	cout << endl;
}
void main()
{
	test3();
}




set的最终测试(涵盖上面所有的接口)

测试如下图,可以发现是符合我们的预期的。

上图代码如下。

#include"Set.h";
#include"Map.h";

void test1()
{
	RBTree<int, int>r;
	r.Insert(make_pair(1, 1));
	r.Insert(make_pair(4, 4));
	r.Insert(make_pair(2, 2));
	r.Insert(make_pair(3, 3));
	const RBTree<int, int>r1(r);

	for (RBTree<int, int>::const_iterator it = r1.begin(); it != r1.end(); it++)
	{
		cout << (*it).first << ' ';
	}
	cout << endl;
}

void test2()
{
	string s[] = { "苹果","香蕉","牛奶","香蕉","橙子","苹果","菠萝"};
	Map<string, int>m;
	
	//对map的插入的检测如下
	for (string& e : s)
	{
		m[e]++;//因为map的operator[]是对map的Insert(或者说是对RBTree的Insert)的一层封装,所以如果检测时发现operator[]没有问题,那map的Insert也肯定是没问题的
	}

	//对map的迭代器部分、map的begin()、end()的检测如下
	for (Map<string, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << it->first << ":" << it->second<<endl;
	}
	cout << endl;

	//对map的删除的检测如下
	m.Erase("香蕉");
	m.Erase("牛奶");
	m.Erase("菠萝");

	//支持map的迭代器后就支持范围for了
	for (pair<string, int>& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
}

void test3()
{
	Set<string>s;

	//对set的插入的检测如下
	s.Insert("香蕉");
	s.Insert("牛奶");
	s.Insert("菠萝");
	s.Insert("苹果");
	s.Insert("草莓");

	//对set的迭代器类RBTreeIterator的operator系列等接口的检测如下
	//对set的begin()等接口的检测如下
	for (Set<string>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << it->first << ' ';
	}
	cout << endl;

	//对set的删除的检测如下
	s.Erase("牛奶");
	s.Erase("菠萝");
	s.Erase("香蕉");
	for (Set<string>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << it->first << ' ';
	}
	cout << endl;

	//set的迭代器实现后,范围for也就被支持了
	for (pair<string, string>& p : s)
	{
		cout << p.first << ' ';
	}
	cout << endl;
}
void main()
{
	test3();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值