STL —— 用一棵红黑树同时封装出map和set

news2024/10/7 10:21:27

文章目录

    • 红黑树源代码
      • RBTree.h
    • 红黑树模板参数的控制
    • 红黑树结点当中存储的数据
    • 模板参数中仿函数的增加
    • 普通迭代器和const迭代器的实现
      • myMap.h
      • mySet.h
      • RBTree.h

红黑树源代码

  • 使用KV模型的红黑树进行封装,同时模拟实现出C++STL库当中的map和set

RBTree.h

//枚举定义结点的颜色
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;

	//结点的颜色
	Colour _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; //红黑树的根结点
};

红黑树模板参数的控制

  • 我们用一棵KV模型的红黑树同时实现map和set,现在将红黑树第二个模板参数的名字改为T
template<class K, class T>
class RBTree
  • T模板参数可能只是键值Key,也可能是由Key和Value共同构成的键值对。如果是set容器,那么它传入底层红黑树的模板参数就是Key和Key
template<class K>
class set
{
public:
	//...
private:
	RBTree<K, K> _t;
};
  • 但如果是map容器,那么它传入底层红黑树的模板参数就是Key以及KeyValue构成的键值对:
template<class K, class V>
class map
{
public:
	//...
private:
	RBTree<K, pair<K, V>> _t;
};

那能不能不要红黑树的第一个模板参数,只保留第二个模板参数呢?

  • 对于set容器来说,省略红黑树的第一个参数当然没问题,因为set传入红黑树的第二个参数与第一个参数是一样的。但是对于map容器来说就不行了,因为map容器所提供的接口当中有些是只要求给出键值Key的,比如find和erase。

红黑树结点当中存储的数据

set容器:K和T都代表键值Key。
map容器:K代表键值Key,T代表由Key和Value构成的键值对。

  • 对于set容器来说,底层红黑树结点当中存储K和T都是一样的,但是对于map容器来说,底层红黑树就只能存储T了。由于底层红黑树并不知道上层容器到底是map还是set,因此红黑树的结点当中直接存储T。

  • 当上层容器是set的时候,结点当中存储的是键值Key;当上层容器是map的时候,结点 当中存储的就是<Key, Value>键值对。

在这里插入图片描述

//红黑树结点的定义
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的时候T就是键值Key,直接用T进行比较即可,但当上层容器是map的时候就不行了,此时我们需要从<Key, Value>键值对当中取出键值Key后,再用Key值进行比较。

  • 因此,上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,这样一来,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了。

  • 仿函数,就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

template<class K, class V>
class map
{
	//仿函数
	struct MapKeyOfT
	{
		const K& operator()(const pair<K, V>& kv) //返回键值对当中的键值Key
		{
			return kv.first;
		}
	};
public:
	//...
private:
	RBTree<K, pair<K, V>, MapKeyOfT> _t;
};
  • 但是对于底层红黑树来说,它并不知道上层容器是map还是set,因此当需要进行两个结点键值的比较时,底层红黑树都会通过传入的仿函数来获取键值Key,进而进行两个结点键值的比较。

  • 因此,set容器也需要向底层红黑树传入一个仿函数,虽然这个仿函数单独看起来没什么用,但却是必不可少的。

template<class K>
class set
{
	//仿函数
	struct SetKeyOfT
	{
		const K& operator()(const K& key) //返回键值Key
		{
			return key;
		}
	};
public:
	//...
private:
	RBTree<K, K, SetKeyOfT> _t;
};
  • 此时,set容器传入底层红黑树的就是set的仿函数,map容器传入底层红黑树的就是map的仿函数。

在这里插入图片描述

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

  • 注意: 所有进行结点键值比较的地方,均需要通过仿函数获取对应结点的键值后再进行键值的比较。

普通迭代器和const迭代器的实现

myMap.h

#pragma once
#include"RBTree.h"

namespace lsl //防止命名冲突
{
	template<class K, class V>
	class map
	{
	public:
		//仿函数
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv) //返回键值对当中的键值Key
			{
				return kv.first;
			}
		};
		// 对类模板取内嵌类型,加typename告诉编译器这里是类型
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator; // const迭代器

		iterator begin()		
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		} 
		//插入函数
		pair<iterator, bool> insert(const pair<const K, V>& kv)
		{
			return _t.Insert(kv);
		}
		//[]运算符重载函数
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}
		//删除函数
		void erase(const K& key)
		{
			_t.Erase(key);
		}
		//查找函数
		iterator find(const K& key)
		{
			return _t.Find(key);
		}
	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};
}

mySet.h

#pragma once

#include "RBTree.h"
namespace lsl //防止命名冲突
{
	template<class K>
	class set
	{
	public:
		//仿函数
		struct SetKeyOfT
		{
			const K& operator()(const K& key) //返回键值Key
			{
				return key;
			}
		};
		// 对类模板取内嵌类型,加typename告诉编译器这里是类型
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;//const迭代器

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

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

		//插入函数
		pair<iterator, bool> insert(const K& key)
		{
			return _t.Insert(key);
		}
		//删除函数
		void erase(const K& key)
		{
			_t.Erase(key);
		}
		//查找函数
		iterator find(const K& key)
		{
			return _t.Find(key);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
}

RBTree.h

#pragma once
#include<iostream>

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)
	{}
};

//正向迭代器
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) //根据所给结点指针构造一个正向迭代器
	{}

	Ref operator*()
	{
		return _node->_data; //返回结点数据的引用
	}
	Ptr operator->()
	{
		return &_node->_data; //返回结点数据的指针
	}
	//判断两个正向迭代器是否不同
	bool operator!=(const Self& s) const
	{
		return _node != s._node; //判断两个正向迭代器所封装的结点是否是同一个
	}
	//判断两个正向迭代器是否相同
	bool operator==(const Self& s) const
	{
		return _node == s._node; //判断两个正向迭代器所封装的结点是否是同一个
	}

	//前置++
	/*
	1. it指向的节点,右子树不为空,下一个右子树的最左节点
	2. it指向的节点,右子树为空,it中的节点所在的子树访问完了,往上找孩子是父亲左的那个祖先
	*/
	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;
	}

	//前置--
	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 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; //const迭代器

	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* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		//返回最左结点的正向迭代器
		return const_iterator(left);
	}
	const_iterator end()const
	{
		//返回由nullptr构造得到的正向迭代器(不严谨)
		return const_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)
	{
		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(); //查找失败
	}

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

		//2、将待插入结点插入到树中
		cur = new Node(data); //根据所给值构造一个结点
		Node* newnode = cur; //记录新插入的结点(便于后序返回)
		if (kot(data) < kot(parent->_data)) //新结点的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)
	{
		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)
	{
		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; //红黑树的根结点
};

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

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

相关文章

网络安全:Web 安全 面试题.(XSS)

网络安全&#xff1a;Web 安全 面试题.&#xff08;XSS&#xff09; 网络安全面试是指在招聘过程中,面试官会针对应聘者的网络安全相关知识和技能进行评估和考察。这种面试通常包括以下几个方面&#xff1a; &#xff08;1&#xff09;基础知识:包括网络基础知识、操作系统知…

axios的基本使用和vue脚手架自带的跨域问题解决

axios的基本使用和vue脚手架自带的跨域问题解决 1. axios 1.1 导入axios npm i axios1.2 创建serve1.js serve1.js const express require(express) const app express()app.use((request,response,next)>{console.log(有人请求服务器1了);console.log(请求来自于,re…

【BES2500x系列 -- RTX5操作系统】深入探索CMSIS-RTOS RTX -- 任务管理篇 -- 线程管理 --(二)

&#x1f48c; 所属专栏&#xff1a;【BES2500x系列】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f49…

SSM图书馆管理系统-计算机毕业设计源码07925

摘 要 “鸟欲高飞先振翅&#xff0c;人求上进先读书”&#xff0c;可见读书对我们的重要性&#xff0c;在这个高速发展的社会&#xff0c;人们也越来越意识到读书的重要性&#xff0c;人们需要汲取各个领域的知识&#xff0c;不断的拓展自己的知识水平&#xff0c;图书馆作为一…

华为---OSPF的DR与BDR(六)

9.6 OSPF的DR与BDR 9.6.1 原理概述 在OSPF的广播类型网络和NBMA类型网络中&#xff0c;如果网络中有n台路由器&#xff0c;若任意两台路由器之间都要建立邻接关系&#xff0c;则需要建立n(n-1)/2个邻接关系&#xff0c;即当路由器很多时&#xff0c;则需要建立和维护的邻接关…

MTK7628+MT7612 加PA定频数据

1、硬件型号TR726A5G121-DPA PC9.02.0017。如下所示&#xff1a; 2、WIFI5.8 AC模式 42&#xff08;5120MHz&#xff09;信道&#xff0c;80带宽 3、WIFI5.8 AC模式 38&#xff08;5190MHz&#xff09;信道&#xff0c;40带宽 4、WIFI5.8 AC模式 36&#xff08;5180 MHz&…

【Linux】进程间通信上 (1.5万字详解)

目录 一.进程间通信介绍 1.1进程间通信的目的 1.2初步认识进程间通信 1.3进程间通信的种类 二.匿名管道 2.1何为管道 2.1实现原理 2.3进一步探寻匿名管道 2.4编码实现匿名管道通信 2.5管道读写特点 2.6基于管道的进程池设计 三.命名管道 3.1实现原理 3.2代码实现 四.…

asp.net core反向代理

新建项目 新建空白的asp.net core web项目 安装Yarp.ReverseProxy包版本为2.2.0-preview.1.24266.1 编写代码 namespace YarpStu01;public class Program {public static void Main(string[] args){var builder WebApplication.CreateBuilder(args);builder.Services.AddRev…

再添国家级认证!亚信安全获国家信息安全服务(数据安全类)一级资质

近日&#xff0c;亚信安全荣获由中国信息安全测评中心颁发的《国家信息安全测评信息安全服务资质证书—数据安全类一级》资质。凭借在数据安全领域的专业地位和技术水平&#xff0c;亚信安全成为为数不多的获得国家信息安全服务资质数据安全类一级的网络安全企业&#xff01; 中…

如何用家政服务小程序源码系统 快速搭建家政自己接单的软件?

随着移动互联网的快速发展&#xff0c;线上预约服务已成为家政行业的新趋势。然而&#xff0c;许多家政企业面临着信息不透明、服务质量参差不齐、管理效率低下等问题。家政服务小程序系统&#xff0c;可以帮助商家快速搭建自己的接单软件&#xff0c;提升服务效率&#xff0c;…

网安大咖说·镜鉴(下)| 把握安全新脉搏:企业CSO的领航之道

网安大咖说镜鉴栏目通过对网安大咖说嘉宾访谈内容的深度提炼&#xff0c;撷取群英论道之精髓&#xff0c;汇聚众智谋策之高远&#xff0c;为从业者提供宝贵的经验和启迪。集思广益、博采众长&#xff0c;意在以镜为鉴&#xff0c;观网安之百态&#xff0c;立防范之策略&#xf…

AI界又放大招?长文本新霸主“橙篇”来了

哈喽&#xff0c;大家好&#xff0c;木易巷来啦&#xff01; 在自媒体的江湖里&#xff0c;每个创作者都是一位独行侠&#xff0c;手握键盘&#xff0c;心怀天下。而我&#xff0c;也不例外&#xff0c;有点空闲时间就去寻找那个能让我笑傲江湖的AI神兵&#xff0c;让我可以摆…

DEtection TRansformer (DETR)与YOLO在目标检测方面的比较

1. 概述 计算机视觉中的目标检测是一个复杂而有趣的领域&#xff0c;它涉及到让计算机能够识别图像中的物体&#xff0c;并确定它们的位置。下面是DETR和YOLO这两种目标检测方法简单比较&#xff1a; 1.1 YOLO YOLO是一种非常流行的目标检测算法&#xff0c;它的核心思想是将…

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中&#xff0c;数据载体是一个较为重要组成部分&#xff0c;ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是&#xff0c;这些…

【源码】 最新麻豆MDYS14源码 油条视频 苹果CMS系统 附搭建教程thinkphp

最新麻豆MDYS14源码 油条视频 苹果CMS系统 附搭建教程 简介&#xff1a; 最新麻豆MDYS14源码 油条视频 苹果CMS系统 附搭建教程 基本介绍&#xff1a; 1. 后台增加自定义参数&#xff0c;对应会员升级页面&#xff0c;以及积分充值 2. 视频&#xff0c;演员&#xff0c;专题…

[分布式网络通讯框架]----Zookeeper客户端基本操作----ls、get、create、set、delete

Zookeeper数据结构 zk客户端常用命令 进入客户端 在bin目录下输入./zkCli.sh 查看根目录下数据ls / 注意:要查看哪一个节点&#xff0c;必须把路径写全 查看节点数据信息 get /第一行代码数据&#xff0c;没有的话表示没有数据 创建节点create /sl 20 /sl为节点的路径&am…

vscode在windows系统上进行C/C++环境配置

随手笔记前言 vscode在windows系统上进行C/C环境配置 步骤如下 第一步 下载安装VSCode 这应该是最简单的一步&#xff0c;相信大家自己就可以完成。如果在vscode官网感觉下载特别慢的话&#xff0c;可以去试一下腾讯软件中心&#xff0c;我都是在这个网页上下载的。下载好之…

【数据库】六、事务与并发控制(封锁)

六、事务与并发控制 文章目录 六、事务与并发控制1.事务1.1事务的ACID特性1.2MySQL事务控制语句开启事务提交事务回滚事务 2.并发控制2.1并发执行可能引起的问题2.1.1丢失更新2.1.2不可重复读2.1.3读脏数据 2.2并发调度的可串行性2.3并发与并行的区分2.4事务的隔离级别 3.封锁3…

Hadoop 2.0 大家族(四)

目录 七、Flume&#xff08;一&#xff09;Flume简介&#xff08;二&#xff09;Flume入门 八、Mahout&#xff08;一&#xff09;Mahout简介&#xff08;二&#xff09;Mahout入门 七、Flume Flume是一个分布式高性能、高可靠的数据传输工具&#xff0c;它可用简单的方式将不同…

【unity小技巧】unity事件系统创建通用的对象交互的功能

文章目录 前言实现1. **InteractEvent 类**&#xff1a;2. **Interact 类**&#xff1a;3. **Player 类**&#xff1a;4. **Chest 类**&#xff1a; 工作流程说明&#xff1a;开单个箱子按钮触发打开很多箱子拾取物品&#xff08;传参&#xff09;参考完结 前言 游戏开发过程中…