<C++> set、map模拟实现

news2024/11/16 4:29:53

目录

一、适配器红黑树

二、红黑树再设计

1. 重新设计 RBTree 的模板参数

2. 仿函数模板参数

3. 正向迭代器

构造

operator*()

operator->() 

operator!=()

operator++() 

operator--()

正向迭代器代码

4. 反向迭代器

构造

operator*

operator->

operator++

operator--

operator!=

反向迭代器代码

5. 红黑树主体

begin()、end()、

rbegin()、rend() 

insert 返回值

find返回值

STL标准库设计

6. 再设计后完整的RBTree代码

三、set、map封装

1. set基础封装

2. map基础封装

3. insert

4. map::operator[] 

5. find

6. erase

7. 测试

一、适配器红黑树

        STL在封装set、map时,直接适配了RBTree——红黑树,这里的红黑树相较上文我们的红黑树,这里实现了构造、拷贝构造、赋值运算符重载、析构、删除节点等函数

//枚举定义结点的颜色
enum Colour
{
	RED,
	BLACK
};

//红黑树结点的定义
template<class K, class V>
struct RBTreeNode
{
	//三叉链
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

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

	//结点的颜色
	int _col; //红/黑

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

//红黑树的实现
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	//构造函数
	RBTree()
		:_root(nullptr)
	{}

	//拷贝构造
	RBTree(const RBTree<K, V>& t)
	{
		_root = _Copy(t._root, nullptr);
	}

	//赋值运算符重载(现代写法)
	RBTree<K, V>& operator=(RBTree<K, V> t)
	{
		swap(_root, t._root);
		return *this;
	}

	//析构函数
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

	//查找函数
	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key < cur->_kv.first) //key值小于该结点的值
			{
				cur = cur->_left; //在该结点的左子树当中查找
			}
			else if (key > cur->_kv.first) //key值大于该结点的值
			{
				cur = cur->_right; //在该结点的右子树当中查找
			}
			else //找到了目标结点
			{
				return cur; //返回该结点
			}
		}
		return nullptr; //查找失败
	}

	//插入函数
	pair<Node*, bool> Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr) //若红黑树为空树,则插入结点直接作为根结点
		{
			_root = new Node(kv);
			_root->_col = BLACK; //根结点必须是黑色
			return make_pair(_root, true); //插入成功
		}
		//1、按二叉搜索树的插入方法,找到待插入位置
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first < cur->_kv.first) //待插入结点的key值小于当前结点的key值
			{
				//往该结点的左子树走
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first) //待插入结点的key值大于当前结点的key值
			{
				//往该结点的右子树走
				parent = cur;
				cur = cur->_right;
			}
			else //待插入结点的key值等于当前结点的key值
			{
				return make_pair(cur, false); //插入失败
			}
		}

		//2、将待插入结点插入到树中
		cur = new Node(kv); //根据所给值构造一个结点
		Node* newnode = cur; //记录新插入的结点(便于后序返回)
		if (kv.first < parent->_kv.first) //新结点的key值小于parent的key值
		{
			//插入到parent的左边
			parent->_left = cur;
			cur->_parent = parent;
		}
		else //新结点的key值大于parent的key值
		{
			//插入到parent的右边
			parent->_right = cur;
			cur->_parent = parent;
		}

		//3、若插入结点的父结点是红色的,则需要对红黑树进行调整
		while (parent&&parent->_col == RED)
		{
			Node* grandfather = parent->_parent; //parent是红色,则其父结点一定存在
			if (parent == grandfather->_left) //parent是grandfather的左孩子
			{
				Node* uncle = grandfather->_right; //uncle是grandfather的右孩子
				if (uncle&&uncle->_col == RED) //情况1:uncle存在且为红
				{
					//颜色调整
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else //情况2+情况3:uncle不存在 + uncle存在且为黑
				{
					if (cur == parent->_left)
					{
						RotateR(grandfather); //右单旋

						//颜色调整
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else //cur == parent->_right
					{
						RotateLR(grandfather); //左右双旋

						//颜色调整
						grandfather->_col = RED;
						cur->_col = BLACK;
					}
					break; //子树旋转后,该子树的根变成了黑色,无需继续往上进行处理
				}
			}
			else //parent是grandfather的右孩子
			{
				Node* uncle = grandfather->_left; //uncle是grandfather的左孩子
				if (uncle&&uncle->_col == RED) //情况1:uncle存在且为红
				{
					//颜色调整
					uncle->_col = parent->_col = BLACK;
					grandfather->_col = RED;

					//继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else //情况2+情况3:uncle不存在 + uncle存在且为黑
				{
					if (cur == parent->_left)
					{
						RotateRL(grandfather); //右左双旋

						//颜色调整
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					else //cur == parent->_right
					{
						RotateL(grandfather); //左单旋

						//颜色调整
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					break; //子树旋转后,该子树的根变成了黑色,无需继续往上进行处理
				}
			}
		}
		_root->_col = BLACK; //根结点的颜色为黑色(可能被情况一变成了红色,需要变回黑色)
		return make_pair(newnode, true); //插入成功
	}

	//删除函数
	bool Erase(const K& key)
	{
		//用于遍历二叉树
		Node* parent = nullptr;
		Node* cur = _root;
		//用于标记实际的待删除结点及其父结点
		Node* delParentPos = nullptr;
		Node* delPos = nullptr;
		while (cur)
		{
			if (key < cur->_kv.first) //所给key值小于当前结点的key值
			{
				//往该结点的左子树走
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_kv.first) //所给key值大于当前结点的key值
			{
				//往该结点的右子树走
				parent = cur;
				cur = cur->_right;
			}
			else //找到了待删除结点
			{
				if (cur->_left == nullptr) //待删除结点的左子树为空
				{
					if (cur == _root) //待删除结点是根结点
					{
						_root = _root->_right; //让根结点的右子树作为新的根结点
						if (_root)
						{
							_root->_parent = nullptr;
							_root->_col = BLACK; //根结点为黑色
						}
						delete cur; //删除原根结点
						return true;
					}
					else
					{
						delParentPos = parent; //标记实际删除结点的父结点
						delPos = cur; //标记实际删除的结点
					}
					break; //进行红黑树的调整以及结点的实际删除
				}
				else if (cur->_right == nullptr) //待删除结点的右子树为空
				{
					if (cur == _root) //待删除结点是根结点
					{
						_root = _root->_left; //让根结点的左子树作为新的根结点
						if (_root)
						{
							_root->_parent = nullptr;
							_root->_col = BLACK; //根结点为黑色
						}
						delete cur; //删除原根结点
						return true;
					}
					else
					{
						delParentPos = parent; //标记实际删除结点的父结点
						delPos = cur; //标记实际删除的结点
					}
					break; //进行红黑树的调整以及结点的实际删除
				}
				else //待删除结点的左右子树均不为空
				{
					//替换法删除
					//寻找待删除结点右子树当中key值最小的结点作为实际删除结点
					Node* minParent = cur;
					Node* minRight = cur->_right;
					while (minRight->_left)
					{
						minParent = minRight;
						minRight = minRight->_left;
					}
					cur->_kv.first = minRight->_kv.first; //将待删除结点的key改为minRight的key
					cur->_kv.second = minRight->_kv.second; //将待删除结点的value改为minRight的value
					delParentPos = minParent; //标记实际删除结点的父结点
					delPos = minRight; //标记实际删除的结点
					break; //进行红黑树的调整以及结点的实际删除
				}
			}
		}
		if (delPos == nullptr) //delPos没有被修改过,说明没有找到待删除结点
		{
			return false;
		}

		//记录待删除结点及其父结点(用于后续实际删除)
		Node* del = delPos;
		Node* delP = delParentPos;

		//调整红黑树
		if (delPos->_col == BLACK) //删除的是黑色结点
		{
			if (delPos->_left) //待删除结点有一个红色的左孩子(不可能是黑色)
			{
				delPos->_left->_col = BLACK; //将这个红色的左孩子变黑即可
			}
			else if (delPos->_right) //待删除结点有一个红色的右孩子(不可能是黑色)
			{
				delPos->_right->_col = BLACK; //将这个红色的右孩子变黑即可
			}
			else //待删除结点的左右均为空
			{
				while (delPos != _root) //可能一直调整到根结点
				{
					if (delPos == delParentPos->_left) //待删除结点是其父结点的左孩子
					{
						Node* brother = delParentPos->_right; //兄弟结点是其父结点的右孩子
						//情况一:brother为红色
						if (brother->_col == RED)
						{
							delParentPos->_col = RED;
							brother->_col = BLACK;
							RotateL(delParentPos);
							//需要继续处理
							brother = delParentPos->_right; //更新brother(否则在本循环中执行其他情况的代码会出错)
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (delParentPos->_col == RED)
							{
								delParentPos->_col = BLACK;
								break;
							}
							//需要继续处理
							delPos = delParentPos;
							delParentPos = delPos->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空
							if ((brother->_right == nullptr) || (brother->_right->_col == BLACK))
							{
								brother->_left->_col = BLACK;
								brother->_col = RED;
								RotateR(brother);
								//需要继续处理
								brother = delParentPos->_right; //更新brother(否则执行下面情况四的代码会出错)
							}
							//情况四:brother为黑色,且其右孩子是红色结点
							brother->_col = delParentPos->_col;
							delParentPos->_col = BLACK;
							brother->_right->_col = BLACK;
							RotateL(delParentPos);
							break; //情况四执行完毕后调整一定结束
						}
					}
					else //delPos == delParentPos->_right //待删除结点是其父结点的左孩子
					{
						Node* brother = delParentPos->_left; //兄弟结点是其父结点的左孩子
						//情况一:brother为红色
						if (brother->_col == RED) //brother为红色
						{
							delParentPos->_col = RED;
							brother->_col = BLACK;
							RotateR(delParentPos);
							//需要继续处理
							brother = delParentPos->_left; //更新brother(否则在本循环中执行其他情况的代码会出错)
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (delParentPos->_col == RED)
							{
								delParentPos->_col = BLACK;
								break;
							}
							//需要继续处理
							delPos = delParentPos;
							delParentPos = delPos->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空
							if ((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							{
								brother->_right->_col = BLACK;
								brother->_col = RED;
								RotateL(brother);
								//需要继续处理
								brother = delParentPos->_left; //更新brother(否则执行下面情况四的代码会出错)
							}
							//情况四:brother为黑色,且其左孩子是红色结点
							brother->_col = delParentPos->_col;
							delParentPos->_col = BLACK;
							brother->_left->_col = BLACK;
							RotateR(delParentPos);
							break; //情况四执行完毕后调整一定结束
						}
					}
				}
			}
		}
		//进行实际删除
		if (del->_left == nullptr) //实际删除结点的左子树为空
		{
			if (del == delP->_left) //实际删除结点是其父结点的左孩子
			{
				delP->_left = del->_right;
				if (del->_right)
					del->_right->_parent = delP;
			}
			else //实际删除结点是其父结点的右孩子
			{
				delP->_right = del->_right;
				if (del->_right)
					del->_right->_parent = delP;
			}
		}
		else //实际删除结点的右子树为空
		{
			if (del == delP->_left) //实际删除结点是其父结点的左孩子
			{
				delP->_left = del->_left;
				if (del->_left)
					del->_left->_parent = delP;
			}
			else //实际删除结点是其父结点的右孩子
			{
				delP->_right = del->_left;
				if (del->_left)
					del->_left->_parent = delP;
			}
		}
		delete del; //实际删除结点
		return true;
	}
	
private:
	//拷贝树
	Node* _Copy(Node* root, Node* parent)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		Node* copyNode = new Node(root->_data);
		copyNode->_parent = parent;
		copyNode->_left = _Copy(root->_left, copyNode);
		copyNode->_right = _Copy(root->_right, copyNode);
		return copyNode;
	}

	//析构函数子函数
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		//建立subRL与parent之间的联系
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		//建立parent与subR之间的联系
		subR->_left = parent;
		parent->_parent = subR;

		//建立subR与parentParent之间的联系
		if (parentParent == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}
	}

	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

		//建立subLR与parent之间的联系
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		//建立parent与subL之间的联系
		subL->_right = parent;
		parent->_parent = subL;

		//建立subL与parentParent之间的联系
		if (parentParent == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}
	}

	//左右双旋
	void RotateLR(Node* parent)
	{
		RotateL(parent->_left);
		RotateR(parent);
	}

	//右左双旋
	void RotateRL(Node* parent)
	{
		RotateR(parent->_right);
		RotateL(parent);
	}

	Node* _root; //红黑树的根结点
};

二、红黑树再设计

1. 重新设计 RBTree 的模板参数

        之前我们的RBTree模板参数是Key-Value模型,但此时我们不得不再考虑设计新的模板参数,因为STL中set、map都是适配红黑树,那么同一个RBTree模板如何同时适配set(Key模型)、map(Key-Value模型)呢?

template<class K, class T>
class RBTree

为什么要设计为K,T模板? 

        根据是set 还是 map,T有两种可能: key 或 pair<K, T>,所以我们需要第一个模板参数K,它绝对是Key。

        设计成K,T是为了适配 find 、erase函数,因为 find 函数需要参数Key,如果以之前的模板参数设计,set调用时很方便,直接传递Key;而map只能传递pair键值对,那么 find 内部还要根据上层是set还是map,来解析传递的模板参数是裸露的Key还是在键值对中的Key,但是对于底层红黑树来说,它并不知道上层容器是map还是set,所以如果不修改模板参数,那么这无疑加重了红黑树中 find 的设计难度!

        所以,set、map都后退一步,全都以统一的模板参数适配RBTree,set多传递一个模板参数Key、map将V修改为pair<K, V>,这样set、map就可以协调统一的适配RBTree,可以说set是跟着map “陪跑”,因为set、map共用的红黑树底层

  • set容器:K和T都代表键值Key
  • map容器:K代表键值Key,T代表由Key和Value构成的键值对
//set
RBTree<K, K> _t;

//map
RBTree<K, pair<K, V>> _t;

 

在红黑树中 typedef RBTreeNode<T> Node,红黑树节点直接接收参数T,更改后代码如下: 

//红黑树结点的定义
template<class T>
struct RBTreeNode
{
	//三叉链
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	//存储的数据
	T _data;

	//结点的颜色
	int _col; //红/黑

	//构造函数
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}
};

 

2. 仿函数模板参数

红黑树重新设计模板参数后,节点存储的是T类型,这个T即可能是Key,也可能是pair<Key, Value>键值对,那么如果在find函数内进行节点的Key大小比较时,如果正确获取Key用来比较大小呢?

        底层红黑树的插入比较时,因为T的类型不确定,所以我们可以模仿list模拟实现时使用的仿函数,来比较大小,但是这里我们不直接在仿函数内比较大小,而是使用 KeyOfT 类,用它创建一个类对象,使用仿函数来获取 T 中 Key,而不是直接进行比较!

        对于set,T 就是key;对于map,T是pair,所以要返回 pair 的 first 

为什么不将仿函数功能复合,直接设计成比大小返回bool类型?

        因为可能有指针等类型,不能按平常的比较来比较,并且在STL的set、map设计中,还有一个用户传递的模板参数compare,它的仿函数是专门用来比较的。

        KeyOfT是红黑树内部用的,两个仿函数各干各的活。如果都交给一个仿函数,那么要排列组合设计很多种类型比较。

当底层红黑树需要进行两个结点之间键值的比较时,都会通过传入的仿函数来获取相应结点的键值,然后再进行比较,下面以红黑树的查找函数为例:

//查找函数
iterator Find(const K& key)
{
	KeyOfT kot;
	Node* cur = _root;
	while (cur)
	{
		if (key < kot(cur->_data)) //key值小于该结点的值
		{
			cur = cur->_left; //在该结点的左子树当中查找
		}
		else if (key > kot(cur->_data)) //key值大于该结点的值
		{
			cur = cur->_right; //在该结点的右子树当中查找
		}
		else //找到了目标结点
		{
			return iterator(cur); //返回该结点
		}
	}
	return end(); //查找失败
}

3. 正向迭代器

构造

红黑树的迭代器封装红黑树节点指针即可(同list模拟实现,迭代器传入三个模板参数是为了更好的设计const迭代器)

//正向迭代器
template<class T, class Ref, class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node; //结点的类型
	typedef __TreeIterator<T, Ref, Ptr> Self; //正向迭代器的类型

	Node* _node; //正向迭代器所封装结点的指针

    //构造函数
    __TreeIterator(Node* node)
	    :_node(node) //根据所给结点指针构造一个正向迭代器
    {}

};
operator*()

当对正向迭代器进行解引用操作时,我们直接返回 对应结点数据的引用

Ref operator*()
{
	return _node->_data; //返回结点数据的引用
}
operator->() 

当对正向迭代器进行->操作时,我们直接返回对应结点数据的指针

Ptr operator->()
{
	return &_node->_data; //返回结点数据的指针
}
operator!=()

operator重载!= 和 ==,比较时直接比较Node*即可(地址最容易比较是否相等)

//判断两个正向迭代器是否不同
bool operator!=(const Self& s) const
{
	return _node != s._node; //判断两个正向迭代器所封装的结点是否是同一个
}
//判断两个正向迭代器是否相同
bool operator==(const Self& s) const
{
	return _node == s._node; //判断两个正向迭代器所封装的结点是否是同一个
}

迭代器设计最难的部分是迭代器的++和--,因为底层适配的是红黑树——非序列式容器,++或--是在树中移动!

 

operator++() 
  • 如果当前节点右子树不为空,++找右子树的最左节点,--找左子树的左右节点
  • 如果当前结点的右子树为空,则++操作后应该在该结点的祖先结点中,找到孩子不在父亲右的祖先
//前置++
Self operator++()
{
	if (_node->_right) //结点的右子树不为空
	{
		//寻找该结点右子树当中的最左结点
		Node* left = _node->_right;
		while (left->_left)
		{
			left = left->_left;
		}
		_node = left; //++后变为该结点
	}
	else //结点的右子树为空
	{
		//寻找孩子不在父亲右的祖先
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent&&cur == parent->_right)
		{
			cur = parent;
			parent = parent->_parent;
		}
		_node = parent; //++后变为该结点
	}
	return *this;
}
operator--()
  1. 如果当前结点的左子树不为空,则--操作后应该找到其左子树当中的最右结点。
  2. 如果当前结点的左子树为空,则--操作后应该在该结点的祖先结点中,找到孩子不在父亲左的祖先
//前置--
Self operator--()
{
	if (_node->_left) //结点的左子树不为空
	{
		//寻找该结点左子树当中的最右结点
		Node* right = _node->_left;
		while (right->_right)
		{
			right = right->_right;
		}
		_node = right; //--后变为该结点
	}
	else //结点的左子树为空
	{
		//寻找孩子不在父亲左的祖先
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent&&cur == parent->_left)
		{
			cur = parent;
			parent = parent->_parent;
		}
		_node = parent; //--后变为该结点
	}
	return *this;
}
正向迭代器代码
template<class T, class Ref, class Ptr>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T, Ref, Ptr> Self;

	/*不管此时__TreeIterator是普通迭代器还是const迭代器,
	我们typedef的Iterator类型都是普通的迭代器*/
	typedef __TreeIterator<T, T&, T*> Iterator;

	//成员变量
	Node* _node;

	//构造函数
	__TreeIterator(Node* node)
		:_node(node)
	{}

	//当__TreeIterator是const迭代器时,这里用普通迭代器构造const迭代器,这是一个构造函数
	//当_TreeIterator是普通迭代器时,这里用普通迭代器构造const迭代器,这是一个拷贝构造函数
	__TreeIterator(const Iterator& it)
		:_node(it._node)
	{}

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

	Self& operator--()
	{
		//左子树不为空
		if (_node->_left)
		{
			//找左子树的最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}
			_node = subRight;
		}
		else //左子树为空,找孩子不在父亲左的祖先
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self& operator++()
	{
		//如果右树不为空
		if (_node->_right)
		{
			// 右树的最左节点(最小节点)
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}

			_node = subLeft;
		}
		else //右树为空
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			// 找孩子是父亲左的那个祖先节点,就是下一个要访问的节点
			while (parent)
			{
				if (cur == parent->_left) break;
				else
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}
			}
			_node = parent;
		}
		return *this;
	}

	bool operator!=(const Self& s) const
	{
		return _node != s._node;
	}

	bool operator==(const Self& s) const
	{
		//拿地址来比较
		return _node == s._node;
	}

};

 

4. 反向迭代器

        在反向迭代器当中只有一个成员变量,那就是反向迭代器封装的正向迭代器。反向迭代器的中成员函数,都是通过调用正向迭代器对应的函数来完成相应功能的

构造

        同list模拟实现,反向迭代器直接适配正向迭代器

#pragma once

template<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;	//反向迭代器自身类型

	//封装普通迭代器
	Iterator _it;

	//构造
	ReverseIterator(Iterator it)
		:_it(it)
	{}

};
operator*
	Ref operator* ()
	{
		return *_it;
	}
operator->
	Ptr operator->()
	{
		return _it.operator->();
	}
operator++

        适配正向迭代器,直接--即可。        

	Self& operator++()
	{
		--_it;
		return *this;
	}
operator--

        适配正向迭代器,直接++即可

	Self& operator--()
	{
		++_it;
		return *this;
	}

 注意:我们实现的都是前置的++或--

operator!=
	bool operator!=(const Self& s) const
	{
		return _it != s._it;
	}

	bool operator==(const Self& s) const
	{
		return _it == s._it;
	}
反向迭代器代码
#pragma once

template<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;	//反向迭代器自身类型

	//封装普通迭代器
	Iterator _it;

	//构造
	ReverseIterator(Iterator it)
		:_it(it)
	{}

	Ref operator* ()
	{
		return *_it;
	}

	Ptr operator->()
	{
		return _it.operator->();
	}

	Self& operator++()
	{
		--_it;
		return *this;
	}

	Self& operator--()
	{
		++_it;
		return *this;
	}

	bool operator!=(const Self& s) const
	{
		return _it != s._it;
	}

	bool operator==(const Self& s) const
	{
		return _it == s._it;
	}

};

 

5. 红黑树主体

我们需要在红黑树的实现当中进行迭代器类型的typedef。需要注意的是,为了让外部能够使用typedef后的正向迭代器类型iterator,我们需要在public区域进行typedef

begin()、end()、
  • begin函数返回中序序列当中第一个结点的正向迭代器,即最左结点。
  • end函数返回中序序列当中最后一个结点下一个位置的正向迭代器,这里直接用空指针构造一个正向迭代器
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node; //结点的类型
public:
	typedef __TreeIterator<T, T&, T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;
	
	iterator begin()
	{
		//寻找最左结点
		Node* left = _root;
		while (left&&left->_left)
		{
			left = left->_left;
		}
		//返回最左结点的正向迭代器
		return iterator(left);
	}
	iterator end()
	{
		//返回由nullptr构造得到的正向迭代器(不严谨)
		return iterator(nullptr);
	}

	const_iterator begin() const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return iterator(leftMin);
	}

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

private:
	Node* _root; //红黑树的根结点
};
rbegin()、rend() 
  • rbegin函数返回中序序列当中最后一个结点的反向迭代器,即最右结点。
  • rend函数返回中序序列当中第一个结点前一个位置的反向迭代器,这里直接用空指针构造一个反向迭代器。
template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T, T&, T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;

	typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
	typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;

	reverse_iterator rbegin()
	{
		Node* right = _root;
		while (right && right->_right)
		{
			right = right->_right;
		}

		return reverse_iterator(iterator(right));
	}

	reverse_iterator rend()
	{
		return reverse_iterator(iterator(nullptr));
	}

	const_reverse_iterator rbegin() const
	{
		Node* right = _root;
		while (right && right->_right)
		{
			right = right->_right;
		}

		return reverse_iterator(iterator(right));
	}

	const_reverse_iterator rend() const
	{
		return reverse_iterator(iterator(nullptr));
	}

private:
	Node* _root; //红黑树的根结点
};
insert 返回值
pair<iterator, bool> insert(const K& key);

 

 

  • 如果插入成功,那么返回新插入的节点的迭代器与 true 构成的 pair
  • 如果插入失败(相应的Key已经存在),那么返回已经存在的 Key 对应的节点迭代器与 false构成的pair

所以在红黑树主体部分,如果 insert 内部要返回 bool 类型时,我们 make_pair 返回 pair<iterator, bool>类型即可

//插入函数
pair<iterator, bool> Insert(const T& data)
{
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return make_pair(iterator(_root), true);
	}

	Node* parent = nullptr;
	Node* cur = _root;

	KeyOfT kot;
	while (cur)
	{
		if (kot(cur->_data) < kot(data))
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (kot(cur->_data) > kot(data))
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return make_pair(iterator(cur), false);
		}
	}

	cur = new Node(data);
	cur->_col = RED;

	Node* newnode = cur;

	if (kot(parent->_data) < kot(data))
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}

	cur->_parent = parent;

	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;
			// u存在且为红
			if (uncle && uncle->_col == RED)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续向上处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else // u不存在 或 存在且为黑
			{
				if (cur == parent->_left)
				{
					//     g
					//   p
					// c
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//     g
					//   p
					//		c
					RotateL(parent);
					RotateR(grandfather);

					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
		else // parent == grandfather->_right
		{
			Node* uncle = grandfather->_left;
			// u存在且为红
			if (uncle && uncle->_col == RED)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续向上处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					// g
					//	  p
					//       c
					RotateL(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
				}
				else
				{
					// g
					//	  p
					// c
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
	}

	_root->_col = BLACK;

	return make_pair(iterator(newnode), true);
}
find返回值

        同理,在返回处 make_pair 即可。

注意:如果没找到需返回end()

iterator Find(const K& key)
{
	Node* cur = _root;
	KeyOfT kot;
	while (cur)
	{
		if (kot(cur->_data) < key)
		{
			cur = cur->_right;
		}
		else if (kot(cur->_data) > key)
		{
			cur = cur->_left;
		}
		else
		{
			return iterator(cur);
		}
	}

	return end();
}
STL标准库设计

        上述所实现的迭代器是有缺陷的,因为理论上我们对end()位置的正向迭代器进行--操作后,应该得到最后一个结点的正向迭代器,但我们实现end()时,是直接返回由nullptr构造得到的正向迭代器的,因此上述实现的代码无法完成此操作。

        下面我们来看看C++SLT库当中的实现逻辑:

        C++STL库当中实现红黑树时,在红黑树的根结点处增加了一个头结点,该头结点的左指针指向红黑树当中的最左结点,右指针指向红黑树当中的最右结点,父指针指向红黑树的根结点。

        在该结构下,实现begin()时,直接用头结点的左孩子构造一个正向迭代器即可,实现rbegin()时,直接用头结点的右孩子构造一个反向迭代器即可(实际是先用该结点构造一个正向迭代器,再用正向迭代器构造出反向迭代器),而实现end()和rend()时,直接用头结点构造出正向和反向迭代器即可。此后,通过对逻辑的控制,就可以实现end()进行--操作后得到最后一个结点的正向迭代器。

        但实现该结构需要更改当前很多函数的逻辑,例如插入结点时,若插入到了红黑树最左结点的左边,或最右结点的右边,此时需要更新头结点左右指针的指向

6. 再设计后完整的RBTree代码

#pragma once
#include<iostream>
#include "ReverseIterator.h"
using namespace std;

enum Colour
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	T _data;
	Colour _col;

	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}
};

// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<K, V>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator<T, T&, T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;

	typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
	typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;

	iterator begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return iterator(leftMin);
	}

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

	const_iterator begin() const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return iterator(leftMin);
	}

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

	reverse_iterator rbegin()
	{
		Node* right = _root;
		while (right && right->_right)
		{
			right = right->_right;
		}

		return reverse_iterator(iterator(right));
	}

	reverse_iterator rend()
	{
		return reverse_iterator(iterator(nullptr));
	}

	const_reverse_iterator rbegin() const
	{
		Node* right = _root;
		while (right && right->_right)
		{
			right = right->_right;
		}

		return reverse_iterator(iterator(right));
	}

	const_reverse_iterator rend() const
	{
		return reverse_iterator(iterator(nullptr));
	}

	//构造函数
	RBTree()
		:_root(nullptr)
	{}

	//拷贝构造
	RBTree(const RBTree<K, T, KeyOfT>& t)
	{
		_root = _Copy(t._root, nullptr);
	}

	//赋值运算符重载(现代写法)
	RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t)
	{
		swap(_root, t._root);
		return *this; //支持连续赋值
	}

	//析构函数
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

	iterator Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return iterator(cur);
			}
		}

		return end();
	}

	//插入函数
	pair<iterator, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}

		Node* parent = nullptr;
		Node* cur = _root;

		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}

		cur = new Node(data);
		cur->_col = RED;

		Node* newnode = cur;

		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		cur->_parent = parent;

		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // u不存在 或 存在且为黑
				{
					if (cur == parent->_left)
					{
						//     g
						//   p
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p
						//		c
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else // parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				// u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						// g
						//	  p
						//       c
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						// g
						//	  p
						// c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(iterator(newnode), true);
	}


	//删除函数
	bool Erase(const K& key)
	{
		KeyOfT kot;
		//用于遍历二叉树
		Node* parent = nullptr;
		Node* cur = _root;
		//用于标记实际的待删除结点及其父结点
		Node* delParentPos = nullptr;
		Node* delPos = nullptr;
		while (cur)
		{
			if (key < kot(cur->_data)) //所给key值小于当前结点的key值
			{
				//往该结点的左子树走
				parent = cur;
				cur = cur->_left;
			}
			else if (key > kot(cur->_data)) //所给key值大于当前结点的key值
			{
				//往该结点的右子树走
				parent = cur;
				cur = cur->_right;
			}
			else //找到了待删除结点
			{
				if (cur->_left == nullptr) //待删除结点的左子树为空
				{
					if (cur == _root) //待删除结点是根结点
					{
						_root = _root->_right; //让根结点的右子树作为新的根结点
						if (_root)
						{
							_root->_parent = nullptr;
							_root->_col = BLACK; //根结点为黑色
						}
						delete cur; //删除原根结点
						return true;
					}
					else
					{
						delParentPos = parent; //标记实际删除结点的父结点
						delPos = cur; //标记实际删除的结点
					}
					break; //进行红黑树的调整以及结点的实际删除
				}
				else if (cur->_right == nullptr) //待删除结点的右子树为空
				{
					if (cur == _root) //待删除结点是根结点
					{
						_root = _root->_left; //让根结点的左子树作为新的根结点
						if (_root)
						{
							_root->_parent = nullptr;
							_root->_col = BLACK; //根结点为黑色
						}
						delete cur; //删除原根结点
						return true;
					}
					else
					{
						delParentPos = parent; //标记实际删除结点的父结点
						delPos = cur; //标记实际删除的结点
					}
					break; //进行红黑树的调整以及结点的实际删除
				}
				else //待删除结点的左右子树均不为空
				{
					//替换法删除
					//寻找待删除结点右子树当中key值最小的结点作为实际删除结点
					Node* minParent = cur;
					Node* minRight = cur->_right;
					while (minRight->_left)
					{
						minParent = minRight;
						minRight = minRight->_left;
					}
					cur->_data = minRight->_data; //将待删除结点的_data改为minRight的_data
					delParentPos = minParent; //标记实际删除结点的父结点
					delPos = minRight; //标记实际删除的结点
					break; //进行红黑树的调整以及结点的实际删除
				}
			}
		}
		if (delPos == nullptr) //delPos没有被修改过,说明没有找到待删除结点
		{
			return false;
		}

		//记录待删除结点及其父结点(用于后续实际删除)
		Node* del = delPos;
		Node* delP = delParentPos;

		//调整红黑树
		if (delPos->_col == BLACK) //删除的是黑色结点
		{
			if (delPos->_left) //待删除结点有一个红色的左孩子(不可能是黑色)
			{
				delPos->_left->_col = BLACK; //将这个红色的左孩子变黑即可
			}
			else if (delPos->_right) //待删除结点有一个红色的右孩子(不可能是黑色)
			{
				delPos->_right->_col = BLACK; //将这个红色的右孩子变黑即可
			}
			else //待删除结点的左右均为空
			{
				while (delPos != _root) //可能一直调整到根结点
				{
					if (delPos == delParentPos->_left) //待删除结点是其父结点的左孩子
					{
						Node* brother = delParentPos->_right; //兄弟结点是其父结点的右孩子
						//情况一:brother为红色
						if (brother->_col == RED)
						{
							delParentPos->_col = RED;
							brother->_col = BLACK;
							RotateL(delParentPos);
							//需要继续处理
							brother = delParentPos->_right; //更新brother(否则在本循环中执行其他情况的代码会出错)
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (delParentPos->_col == RED)
							{
								delParentPos->_col = BLACK;
								break;
							}
							//需要继续处理
							delPos = delParentPos;
							delParentPos = delPos->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空
							if ((brother->_right == nullptr) || (brother->_right->_col == BLACK))
							{
								brother->_left->_col = BLACK;
								brother->_col = RED;
								RotateR(brother);
								//需要继续处理
								brother = delParentPos->_right; //更新brother(否则执行下面情况四的代码会出错)
							}
							//情况四:brother为黑色,且其右孩子是红色结点
							brother->_col = delParentPos->_col;
							delParentPos->_col = BLACK;
							brother->_right->_col = BLACK;
							RotateL(delParentPos);
							break; //情况四执行完毕后调整一定结束
						}
					}
					else //delPos == delParentPos->_right //待删除结点是其父结点的左孩子
					{
						Node* brother = delParentPos->_left; //兄弟结点是其父结点的左孩子
						//情况一:brother为红色
						if (brother->_col == RED) //brother为红色
						{
							delParentPos->_col = RED;
							brother->_col = BLACK;
							RotateR(delParentPos);
							//需要继续处理
							brother = delParentPos->_left; //更新brother(否则在本循环中执行其他情况的代码会出错)
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (delParentPos->_col == RED)
							{
								delParentPos->_col = BLACK;
								break;
							}
							//需要继续处理
							delPos = delParentPos;
							delParentPos = delPos->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空
							if ((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							{
								brother->_right->_col = BLACK;
								brother->_col = RED;
								RotateL(brother);
								//需要继续处理
								brother = delParentPos->_left; //更新brother(否则执行下面情况四的代码会出错)
							}
							//情况四:brother为黑色,且其左孩子是红色结点
							brother->_col = delParentPos->_col;
							delParentPos->_col = BLACK;
							brother->_left->_col = BLACK;
							RotateR(delParentPos);
							break; //情况四执行完毕后调整一定结束
						}
					}
				}
			}
		}
		//进行实际删除
		if (del->_left == nullptr) //实际删除结点的左子树为空
		{
			if (del == delP->_left) //实际删除结点是其父结点的左孩子
			{
				delP->_left = del->_right;
				if (del->_right)
					del->_right->_parent = delP;
			}
			else //实际删除结点是其父结点的右孩子
			{
				delP->_right = del->_right;
				if (del->_right)
					del->_right->_parent = delP;
			}
		}
		else //实际删除结点的右子树为空
		{
			if (del == delP->_left) //实际删除结点是其父结点的左孩子
			{
				delP->_left = del->_left;
				if (del->_left)
					del->_left->_parent = delP;
			}
			else //实际删除结点是其父结点的右孩子
			{
				delP->_right = del->_left;
				if (del->_left)
					del->_left->_parent = delP;
			}
		}
		delete del; //实际删除结点
		return true;
	}

private:

	//拷贝树
	Node* _Copy(Node* root, Node* parent)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		Node* copyNode = new Node(root->_data);
		copyNode->_parent = parent;
		copyNode->_left = _Copy(root->_left, copyNode);
		copyNode->_right = _Copy(root->_right, copyNode);
		return copyNode;
	}

	//析构函数子函数
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

	//左单旋
	void RotateL(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		parent->_right = curleft;
		if (curleft)
		{
			curleft->_parent = parent;
		}

		cur->_left = parent;

		Node* ppnode = parent->_parent;

		parent->_parent = cur;


		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;

			}

			cur->_parent = ppnode;
		}
	}

	//右单旋
	void RotateR(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_left;
		Node* curright = cur->_right;

		parent->_left = curright;
		if (curright)
			curright->_parent = parent;

		Node* ppnode = parent->_parent;
		cur->_right = parent;
		parent->_parent = cur;

		if (ppnode == nullptr)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}

			cur->_parent = ppnode;
		}
	}

	//检查红黑树颜色
	bool CheckColour(Node* root, int blacknum, int benchmark)
	{
		if (root == nullptr)
		{
			if (blacknum != benchmark)
				return false;

			return true;
		}

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

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

		return CheckColour(root->_left, blacknum, benchmark)
			&& CheckColour(root->_right, blacknum, benchmark);
	}

	bool IsBalance()
	{
		return IsBalance(_root);
	}

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

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

		// 基准值
		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++benchmark;

			cur = cur->_left;
		}

		return CheckColour(root, 0, benchmark);
	}

	int Height()
	{
		return Height(_root);
	}

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

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

private:
	Node* _root = nullptr;

public:
	int _rotateCount = 0;
};

三、set、map封装

1. set基础封装

#include "RBTree.h"

namespace my_set
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::reverse_iterator reverse_iterator;

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

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

		reverse_iterator rbegin()
		{
			return _t.rbegin();
		}

		reverse_iterator rend()
		{
			return _t.rend();
		}

		pair<iterator, bool> insert(const K& key)
		{}

		void erase(const K& key)
		{}

		iterator find(const K& key)
		{}

	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
}

2. map基础封装

#include "RBTree.h"

namespace my_map
{
	template<class K, class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::reverse_iterator reverse_iterator;
		
		iterator begin()
		{
			return _t.begin();
		}

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

		reverse_iterator rbegin()
		{
			return _t.rbegin();
		}

		reverse_iterator rend()
		{
			return _t.rend();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{}

		V& operator[](const K& key)
		{}

		void erase(const K& key)
		{}

		iterator find(const K& key)
		{}

	private:
		RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
}

3. insert

set:

pair<iterator, bool> insert(const K& key)
{
	return _t.Insert(key);
}

        set 的迭代器 typedef 了红黑树的 const_iterator  

typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;

map: 

pair<iterator, bool> insert(const pair<K, V>& kv)
{
	return _t.Insert(kv);
}

        map 的 iterator 没有 typedef 红黑树的 const_iterator 

typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;

编译: 

 

问题的引发:在封装map的operator[] 函数时,需要用到insert函数的pair<iterator, bool>返回值,但是此时 set 的返回处出现问题 

C2440    “return”: 无法从“std::pair<__TreeIterator<T,T &,T *>,bool>”转换为“std::pair<__TreeIterator<T,const T &,const T *>,bool>” 

摸索:

        set 底层适配 RBtree 结构 ,所以 set 的 insert 调用的是 RBTree 的 insert 函数,又 RBTree 的 insert 函数返回的是 pair<iterator, bool> 类型(这里的 iterator是红黑树返回的普通迭代器类型)

        (注意:如果在 set.h 文件中,此时 pair 的 iterator 类型其实是 const_iterator 类型,因为我们是根据 STL 中的 set 设计来设计我们的 set ,STL 中 set 的 iterator 其实是 const_iterator 的 typedef,是为了保证set的key不被修改,因为底层红黑树节点是靠key来构建的,一旦key被修改,那么整个树的结构就会混乱无序) 

        所以 set 的 insert 在 return _t.Insert 时,在 pair 的构造函数的初始化列表处会发生普通迭代器构造 const 迭代器的过程

        所以我们需要在红黑树迭代器类中再手写一个构造函数,参数是普通迭代器,用来支持普通迭代器向const迭代器转换

迭代器类中,默认生成的拷贝构造就已经够用了,因为成员变量类型是Node*,是内置类型,可以浅拷贝,那么为什么还要设计迭代器的拷贝构造?

        其实这里并不是拷贝构造,而是重载构造函数。当 set 的 insert 返回时,我们先来看pair类的构造函数:

此时T1类型是const_iterator
template <class T1, class T2>
struct pair
{
     typedef T1 first_type;
     typedef T2 second_type;

     T1 first;
     T2 second;

     pair()
        : first(T1())    
        , second(T2())
     {}
 
     pair(const T1& a, const T2& b)
        : first(a)    此时的参数a是普通iterator
        , second(b)   first是const_iterator
     {}
};

        我们发现,在pair 的构造函数的初始化列表处,出现了以普通迭代器构造const迭代器的过程,而我们的迭代器此时并没有专门接收迭代器类型的构造函数 

        所以,我们要在红黑树的迭代器类中实现一个迭代器构造函数,支持从普通迭代器转为const迭代器功能 

我们如何在一个const迭代器类中获得普通迭代器类型?

        在迭代器类设计时,typedef一个普通迭代器即可

	//不管此时__TreeIterator是普通迭代器还是const迭代器,
	//我们typedef的Iterator类型都是普通的迭代器
	typedef __TreeIterator<T, T&, T*> Iterator;

        不管此时__TreeIterator 是普通迭代器还是 const 迭代器,我们 typedef 的 Iterator 类型都是普通的迭代器

	//当__TreeIterator是const迭代器时,这里用普通迭代器构造const迭代器,这是一个构造函数
	//当_TreeIterator是普通迭代器时,这里用普通迭代器构造const迭代器,这是一个拷贝构造函数
	__TreeIterator(const Iterator& it)
		:_node(it._node)
	{}
  • 当 __TreeIterator 是const迭代器时,这里用普通迭代器构造const迭代器,这是一个构造函数,因为我们在迭代器类中已经写过构造函数了,所以还需要重载一个以迭代器为参数的构造函数
  • 当 __TreeIterator 是普通迭代器时,这里用普通迭代器构造const迭代器,这是一个拷贝构造函数

4. map::operator[] 

        map::operator[]功能我们在map介绍时已经讲过了,返回 insert 返回的 iterator 的 second 数据的引用,即不管insert是否成功,我们都能获取Key的value的引用

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

5. find

        调用适配的红黑树的Find函数即可

iterator find(const K& key)
{
	return _t.Find(key);
}

6. erase

        调用适配的红黑树的Erase函数即可

void erase(const K& key)
{
	_t.Erase(key);
}

7. 测试 

#include <iostream>
 
#include "MyMap.h"
#include "MySet.h"

int main()
{
	//验证map
	my_map::map<int, int> m;
	m.insert(make_pair(1, 1));
	m.insert(make_pair(3, 3));
	m.insert(make_pair(4, 4));
	m.insert(make_pair(3, 8));
	m.insert(make_pair(5, 1));

	my_map::map<int, int>::reverse_iterator mit = m.rbegin();
	while (mit != m.rend())
	{
		cout << mit->first << ":" << mit->second << endl;
		++mit;
	}

	cout << endl;


	m[1] = 100;
	m[99] = 120;
	m[3] = 4;

	for (const auto& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;

	//验证set
	my_set::set<int> s;
	s.insert(5);
	s.insert(15);
	s.insert(54);
	s.insert(23);
	s.insert(0);
	s.insert(9);
	s.insert(4);

	my_set::set<int>::iterator sit = s.begin();
	while (sit != s.end())
	{
		cout << *sit << " ";
		++sit;
	}

	cout << endl;

	for (const auto& e : s)
	{
		cout << e << " ";
	}
	return 0;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2121630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机械厂如何做好企业级数据治理系统落地

生产制造企业的数据治理面临着诸多难点与挑战&#xff0c;今天以某国营机械厂为例&#xff0c;分享机械制造企业数据治理经验。某机械厂是国家投资的国有大型企业&#xff0c;业务范围覆盖到零部件的研发、测试、生产制造&#xff0c;属于典型的离散制造企业。 鉴于近年来国家…

设计友好型相亲交友应用的关键要素揭秘

在设计一款友好型的相亲交友应用时&#xff0c;关键在于创造一个安全、舒适且高效的用户体验。以下是几个核心要素&#xff0c;它们共同构成了一个成功的相亲交友应用的基础&#xff08;编辑h17711347205&#xff09;。 1. 简洁直观的用户界面&#xff08;UI&#xff09; 用户…

【Java数据结构】泛型的进阶部分(泛型通配符)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

protobuf使用

我下载的是 protobuf-27.4 以下使用vs2022 根据readme&#xff0c;执行如下命令 "C:\Program Files\CMake\bin\cmake.exe" -G "Visual Studio 17 2022" -DCMAKE_INSTALL_PREFIXC:\Users\x\Downloads\install C:\Users\x\Downloads\protobuf-27.4 -D…

【动态规划】子序列问题(数组中不连续的一段)

子序列问题 1.最长递增子序列2.摆动序列3.最长递增子序列的个数4.最长数对链 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.最长递增子序列…

Java+Swing学生信息管理系统

JavaSwing学生信息管理系统 一、系统介绍二、功能展示1.管理员登陆2.学生信息查询3.学生信息添加4.学生信息修改5.删除 三、系统实现1.StudentFrame .java 四、其它1.其他系统实现 一、系统介绍 该系统实现了管理员系统登陆、学生信息查询、学生信息添加、学生信息修改、学生信…

【代码随想录训练营第42期 Day55打卡 - 图论Part5 - 并查集的应用

目录 一、并查集 适用范围 三大基本操作 二、经典题目 题目&#xff1a;卡码网 107. 寻找存在的路径 题目链接 题解&#xff1a;并查集 三、小结 一、并查集 适用范围 动态连通性问题&#xff1a;并查集可以判断两个节点是否在同一个连通分量中&#xff0c;这在处理网络…

对耳朵伤害最小的耳机类型,开放式耳机最大程度保护耳道健康

开放式耳机是目前比较流行的耳机种类&#xff0c;其特点是通过采用海绵状的微孔发泡塑料制作透声耳垫&#xff0c;或利用骨传导、气传导等技术传音&#xff0c;不使用厚重的染音垫&#xff0c;没有与外界的隔绝感&#xff0c;佩戴者在享受音乐的同时&#xff0c;也能听到一部分…

WordPress自动备份至群辉NAS

目录 一、配置群辉NAS 1、开启远程管理 2、开启群辉NAS的FTP服务 3、新增网址备份路径 4、新增备份账户 5、设定备份账户能访问的资源 二、插件安装 三、配置插件 四、手工备份 五、自动备份 六、插件中的备份管理 七、数据还原 (本文讲述了WordPress搭建的网址,…

《数字信号处理》学习06-因果系统与稳定系统

目录 一&#xff0c;因果系统 二&#xff0c;稳定系统 之前学习了系统中的线性时不变系统&#xff08; 系统&#xff09;&#xff0c;接下来学习线性时不变系统&#xff08; 系统&#xff09;中的因果系统与稳定系统。&#xff08;非LTI系统这里暂时不作为学习的要求&#xf…

Java实现自定义线程池

Java实现自定义线程池 ThreadPool public interface ThreadPool {void execute(Runnable runnable);void shutdown();int getInitSize();int getMaxSize();int getCoreSize();int getQueueSize();int getActiveCount();boolean isShutdown(); }RunnableQueue public interfac…

Windows10 Paddlepaddle-GPU CUDA CUDNN 版本选择

最终选择&#xff1a; 在创建的新环境下 python 3.8.* paddlepaddle-gpu 2.5.1.post120 CUDA 12.0 CUDNN 8.9&#xff08;需配合CUDA的版本&#xff09; 1. 本机GPU硬件信息 打开NVIDIA Control Panel->System Information->Components&#xff0c;…

深度解析:如何注册并培育亚马逊测评买家号?

在亚马逊这个全球热门的电商平台上&#xff0c;产品评价对于卖家而言至关重要&#xff0c;它直接影响着产品的曝光率、转化率以及消费者的购买意愿。因此&#xff0c;亚马逊测评账号的注册与养号成为了许多卖家关注的焦点。本文将介绍亚马逊测评账号的注册流程以及高效养号的一…

[数据集][目标检测]百事可乐可口可乐瓶子检测数据集VOC+YOLO格式195张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;195 标注数量(xml文件个数)&#xff1a;195 标注数量(txt文件个数)&#xff1a;195 标注类别…

RS485工业通信网关原理详解-天拓四方

一、引言 随着工业自动化技术的飞速发展&#xff0c;工业通信网关作为连接各种设备和系统的关键节点&#xff0c;发挥着越来越重要的作用。RS485工业通信网关作为其中的佼佼者&#xff0c;以其高可靠性、长距离传输能力和抗干扰能力强的特点&#xff0c;在工业自动化、楼宇自控…

经纬恒润高压电池管理系统,助力新能源汽车飞速发展

随着新能源汽车行业的快速发展&#xff0c;电池管理系统作为关键技术之一&#xff0c;其重要性日益凸显。经纬恒润自主研发的高压电池管理系统&#xff08;Battery Management System&#xff0c;BMS&#xff09;&#xff0c;凭借卓越的性能与先进的技术&#xff0c;在新能源汽…

【区块链通用服务平台及组件】微言科技数据智能中台

人工智能技术中的机器学习、深度学习依赖于海量数据进行模型训练&#xff0c;仅依靠某一机构的数据&#xff0c;无法实现模型、 算法的快速突破。然而数据要素流通涉及多方主体、多个环节&#xff0c;共享环境复杂&#xff0c;同时数据产品具有极易复制、非排他性、难追溯等特征…

为明天做好准备,摆脱传统财务规划的不足

对于企业规划和财务团队来说&#xff0c;自动化工具和创新技术虽说都能够有力支持企业实现数字化转型&#xff0c;进行符合时代发展的战略规划&#xff0c;但同时也伴随着一定的限制。回溯上个世界七十年代&#xff0c;电子表格的问世改变了经济世界的管理模式&#xff0c;带来…

数据智能驱动的工业互联网:能否真正解决企业成本问题?

数据智能驱动的工业互联网&#xff1a;能否真正解决企业成本问题&#xff1f; 前言数据智能驱动的工业互联网 前言 工业互联网作为推动制造业转型升级的关键力量&#xff0c;正逐渐展现出其巨大的潜力和影响力。随着信息技术的不断发展和应用&#xff0c;工业互联网的概念应运…

ctfshow-web入门-sql注入(web241、web242、web243)delete file 注入

目录 1、web241 2、web242 3、web243 1、web241 //删除记录$sql "delete from ctfshow_user where id {$id}"; 这里是 delete 语句&#xff0c;查询出来的东西不会有回显&#xff0c;因此采用盲注。如果采用布尔盲注&#xff0c;我们需要根据页面的回显情况来判…