用红黑树封装map和set

news2025/2/24 18:30:22

文章目录:

  • 红黑树模拟实现完整代码
  • 红黑树参数适配改造
  • 仿函数
  • 正向迭代器
  • 反向迭代器
  • 红黑树封装后的代码
  • map完整代码
  • set完整代码

红黑树模拟实现完整代码

如下是红黑树kv模型的模拟实现完整代码,现在我们需要基于此代码封装出map和set基本的接口实现。

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

enum Color
{
	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;
	// 存储节点颜色
	Color _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;

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

		// 让parent的右指针指向subRL,判断一下subRL是否为空
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		// subR的左指针链接parent
		subR->_left = parent;
		parent->_parent = subR;

		// parent为根的情况,更新根节点,让根节点指向空
		if (_root == parent)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else //若parent为一棵子树,则链接与parentParent的关系
		{
			if (parentParent->_right == parent)
				parentParent->_right = subR;
			else
				parentParent->_left = subR;

			subR->_parent = parentParent;
		}
	}

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

		// 将subLR链接到parent的左边,这里注意subLR可能为空的情况,需要判断一下
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		// 将parent这棵子树链接到subL的右指针
		subL->_right = parent;
		parent->_parent = subL;

		// 若parent为根,则更新新的根节点
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else //若parent为一棵子树,则链接与parentParent的关系
		{
			if (parentParent->_right == parent)
				parentParent->_right = subL;
			else
				parentParent->_left = subL;

			subL->_parent = parentParent;
		}
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << " -> " << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	void _Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}
public:
	void InOrder()
	{
		_InOrder(_root);
	}

	RBTree()
		:_root(nullptr)
	{}

	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

	Node* find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
				cur = cur->_right;
			else if (cur->_kv.first > key)
				cur = cur->_left;
			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);
		}

		Node* cur = _root;
		Node* parent = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
				// 待插入的节点已经存在,则返回该节点的指针,插入失败
				return make_pair(cur, false);
		}

		// 走到这里就已经找到了将要插入的位置
		Node* newnode = new Node(kv);
		newnode->_col = RED;
		// 判断待插入节点与parent指向值的大小,将待插入节点插入到正确的位置
		if (parent->_kv.first < kv.first)
		{
			parent->_right = newnode;
			newnode->_parent = parent;
		}
		else
		{
			parent->_left = newnode;
			newnode->_parent = parent;
		}
		cur = newnode;

		// 若父亲存在且为红色就需要进行处理
		while (parent && parent->_col == RED)
		{
			// 若父节点为红色,则祖父节点一定存在,不需要进行判断
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				//情况1:uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上进行处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:uncle不存在或者uncle存在且为黑
				{
					// 情况2:右单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else // 左右双旋
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else // grandfather->_right == parent;
			{
				Node* uncle = grandfather->_left;
				// 情况1
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上进行处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else // cur == parent->_left
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}

		// 将根节点的颜色处理为黑色
		_root->_col = BLACK;
		return make_pair(newnode, true);
	}

	bool erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		//用于标记实际的待删除结点及其父结点
		Node* deleteParent = nullptr;
		Node* deleteNode = nullptr;
		while (cur)
		{
			if (key < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_kv.first)
			{
				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
					{
						deleteParent = parent; //标记实际删除结点的父结点
						deleteNode = 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
					{
						deleteParent = parent; //标记实际删除结点的父结点
						deleteNode = 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;
					cur->_kv.second = minRight->_kv.second;
					deleteParent = minParent;
					deleteNode = minRight;
					break;
				}
			}
		}
		if (deleteNode == nullptr)
		{
			return false;
		}

		//记录待删除结点及其父结点
		Node* del = deleteNode;
		Node* delP = deleteParent;

		//调整红黑树
		if (deleteNode->_col == BLACK) //删除的是黑色结点
		{
			if (deleteNode->_left)
				deleteNode->_left->_col = BLACK;

			else if (deleteNode->_right) //待删除结点有一个红色的右孩子(不可能是黑色)
			{
				deleteNode->_right->_col = BLACK;
			}
			else //待删除结点的左右均为空
			{
				while (deleteNode != _root)
				{
					if (deleteNode == deleteParent->_left)
					{
						Node* brother = deleteParent->_right;
						//情况一:brother为红色
						if (brother->_col == RED)
						{
							deleteParent->_col = RED;
							brother->_col = BLACK;
							RotateL(deleteParent);

							brother = deleteParent->_right; //更新brother
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (deleteParent->_col == RED)
							{
								deleteParent->_col = BLACK;
								break;
							}

							deleteNode = deleteParent;
							deleteParent = deleteNode->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空
							if ((brother->_right == nullptr) || (brother->_right->_col == BLACK))
							{
								brother->_left->_col = BLACK;
								brother->_col = RED;
								RotateR(brother);

								brother = deleteParent->_right; //更新brother
							}
							//情况四:brother为黑色,且其右孩子是红色结点
							brother->_col = deleteParent->_col;
							deleteParent->_col = BLACK;
							brother->_right->_col = BLACK;
							RotateL(deleteParent);
							break;
						}
					}
					else  //待删除结点是其父结点的左孩子
					{
						Node* brother = deleteParent->_left;
						//情况一:brother为红色
						if (brother->_col == RED)
						{
							deleteParent->_col = RED;
							brother->_col = BLACK;
							RotateR(deleteParent);
							//需要继续处理
							brother = deleteParent->_left;
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (deleteParent->_col == RED)
							{
								deleteParent->_col = BLACK;
								break;
							}

							deleteNode = deleteParent;
							deleteParent = deleteNode->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空
							if ((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							{
								brother->_right->_col = BLACK;
								brother->_col = RED;
								RotateL(brother);

								brother = deleteParent->_left;
							}
							//情况四:brother为黑色,且其左孩子是红色结点
							brother->_col = deleteParent->_col;
							deleteParent->_col = BLACK;
							brother->_left->_col = BLACK;
							RotateR(deleteParent);
							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* _root;
};

红黑树参数适配改造

这里我们需要用同一棵红黑树来实现map和set,但是map是kv模型,而set是key模型。我们实现的红黑树是kv模型,为了同时适配k的模型,我们需要对红黑树的模板参数做一些处理。
在这里插入图片描述

  1. 这里需要控制 map 和 set 传入红黑树底层结构的模板参数,为了与之前的 kv 模型参数进行区分,将红黑树的第二个模板参数改为T。则T参数可能存储键值 key,也有可能存储键值对 key-value。
  2. map 传递给红黑树的T模板参数是 pair<K,V> , set 传递给红黑树的T模板参数是 K 。
template<class K>
class set
{
private:
	RBTree<K, K> _t;
};

template<class K,class V>
class map
{
private:
	RBTree<K, pair<const K,V>> _t;
};

❓为什么不去掉红黑树的第一个模板参数,只保留第二个模板参数呢?

不能!红黑树中的第二个模板参数是一个键值对,对于 set 容器来说,省略红黑树第一个模板参数是没有任何问题的,但是对于 map 容器而言,若去掉红黑树的第二个模板参数,那么就无法得到 key 的类型,像 find、erase 这样的接口中是需要键值 key 的,所以第一个模板参数不能够省略。

仿函数

红黑树中的模板参数T可能是 key ,也可能是 <key,value>键值对。在红黑树的实现中需要多次用 key 值进行比较,那我们应该怎样拿到节点的 key 呢?为了解决这个问题,上层容器 map 和 set 需要向底层结构红黑树提供一个仿函数去获取T模板的键值 key 。

在这里插入图片描述

当上层容器是 set 时,红黑树中的 T 就是键值 key ,因此 set 的仿函数直接返回 K 值就可以了。

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

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

当上层容器是 map 时,红黑树中的 T 储存的就是键值对 pair<key,value> ,因此 map 的仿函数需要取出键值对中的第一个参数 key 。

template<class K,class V>
class map
{
	struct MapKeyOfT
	{
		const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};

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

✅仿函数应该如何使用呢?以下我们以红黑树的查找为例来看看仿函数的使用方法:

iterator find(const K& key)
{
	KeyOfT kot; // 定义一个仿函数对象
	Node* cur = _root;
	while (cur)
	{
		if (kot(cur->_data) < key) // 利用仿函数对象去取_data中用来进行比较的参数
			cur = cur->_right;
		else if (kot(cur->_data) > key)
			cur = cur->_left;
		else
			return iterator(cur);
	}
	return end();
}

正向迭代器

✔️红黑树的迭代器即对红黑树中节点指针进行封装,便于使用。

// 正向迭代器
template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef Ref reference;
	typedef Ptr pointer;

	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. 若当前节点的右子树不为空,则 ++ 之后应是其右子树中的最左节点。
  2. 若当前节点的右子树为空,则 ++ 之后应是该节点的祖先节点中,孩子不在父节点右的祖先。

实现代码:

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 && parent->_right == cur)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

✔️红黑树迭代器中 - - 的实现恰好与 ++ 相反,根据红黑树中序遍历序列找到当前节点的前一个节点。

- - 操作的具体步骤:

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

注意: 此处的 ++ 操作和 - - 操作和STL中的实现是不一样的,相对库里面的实现是有差距的,这里只是一个简单的模拟。想要看STL库里面的实现可以去查一下。

正向迭代器完整代码:

template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef Ref reference;
	typedef Ptr pointer;

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

	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 && parent->_right == cur)
			{
				cur = 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;
	}
};

反向迭代器

红黑树的反向迭代器是由正向迭代器封装的,因此红黑树的反向迭代器也是一个迭代器适配器。和之前学习的 priority_queue、stack、queue 都是属于适配器。

// 反向迭代器 - 迭代器适配器
template<class Iterator>
struct ReverseIterator
{
	typedef typename Iterator::reference Ref; // 节点指针的引用
	typedef typename Iterator::pointer Ptr;  // 节点指针
	typedef typename ReverseIterator<Iterator> Self; // 反向迭代器类型

	ReverseIterator(Iterator it)
		:_it(it) // 用正向迭代器封装一个反向迭代器
	{}

	Ref operator*()
	{
		return *_it; // 调用正向迭代器的operator* 返回节点数据的引用
	}

	Ptr operator->()
	{
		return _it.operator->(); // 调用正向迭代器的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;
	}

	Iterator _it; 
};

☃️反向迭代器只有一个模板参数,即正向迭代器的类型。因此反向迭代器是不知道节点的引用类型和节点的指针类型的,这时我们需要对正向迭代器中这两个类型进行 typedef ,则反向迭代器可通过正向迭代器获取节点引用类型和节点指针类型。

template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef Ref reference; // 节点指针引用
	typedef Ptr pointer; // 节点指针
}

红黑树封装后的代码

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

enum Color
{
	RED,
	BLACK,
};

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

	// 存储数据的键值对
	T _data;
	// 存储节点颜色
	Color _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 Ref reference;
	typedef Ptr pointer;

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

	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 && parent->_right == cur)
			{
				cur = 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;
	typedef ReverseIterator<iterator> reverse_iterator;

	iterator begin()
	{
		// 返回红黑树的最左节点
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);
	}

	iterator end()
	{
		return iterator(nullptr); // 用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));
	}

private:
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		// 让parent的右指针指向subRL,判断一下subRL是否为空
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		// subR的左指针链接parent
		subR->_left = parent;
		parent->_parent = subR;

		// parent为根的情况,更新根节点,让根节点指向空
		if (_root == parent)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else //若parent为一棵子树,则链接与parentParent的关系
		{
			if (parentParent->_right == parent)
				parentParent->_right = subR;
			else
				parentParent->_left = subR;

			subR->_parent = parentParent;
		}
	}


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

		// 将subLR链接到parent的左边,这里注意subLR可能为空的情况,需要判断一下
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		// 将parent这棵子树链接到subL的右指针
		subL->_right = parent;
		parent->_parent = subL;

		// 若parent为根,则更新新的根节点
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else //若parent为一棵子树,则链接与parentParent的关系
		{
			if (parentParent->_right == parent)
				parentParent->_right = subL;
			else
				parentParent->_left = subL;

			subL->_parent = parentParent;
		}
	}

	void _Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}
public:
	RBTree()
		:_root(nullptr)
	{}

	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

	iterator find(const K& key)
	{
		KeyOfT kot;
		Node* cur = _root;
		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);
		}

		KeyOfT kot;
		Node* cur = _root;
		Node* parent = _root;
		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);
		}

		// 走到这里就已经找到了将要插入的位置
		Node* newnode = new Node(data);
		newnode->_col = RED;
		// 判断待插入节点与parent指向值的大小,将待插入节点插入到正确的位置
		if (kot(parent->_data) < kot(data))
		{c
			parent->_right = newnode;
			newnode->_parent = parent;
		}
		else
		{
			parent->_left = newnode;
			newnode->_parent = parent;
		}
		cur = newnode;

		// 若父亲存在且为红色就需要进行处理
		while (parent&& parent->_col == RED)
		{
			// 若父节点为红色,则祖父节点一定存在,不需要进行判断
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				//情况1:uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上进行处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:uncle不存在或者uncle存在且为黑
				{
					// 情况2:右单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else // 左右双旋
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else // grandfather->_right == parent;
			{
				Node* uncle = grandfather->_left;
				// 情况1
				if (uncle&& uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上进行处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else // cur == parent->_left
					{
						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)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		//用于标记实际的待删除结点及其父结点
		Node* deleteParent = nullptr;
		Node* deleteNode = nullptr;
		while (cur)
		{
			if (key < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_kv.first)
			{
				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
					{
						deleteParent = parent; //标记实际删除结点的父结点
						deleteNode = 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
					{
						deleteParent = parent; //标记实际删除结点的父结点
						deleteNode = 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;
					cur->_kv.second = minRight->_kv.second;
					deleteParent = minParent;
					deleteNode = minRight;
					break;
				}
			}
		}
		if (deleteNode == nullptr)
		{
			return false;
		}

		//记录待删除结点及其父结点
		Node* del = deleteNode;
		Node* delP = deleteParent;

		//调整红黑树
		if (deleteNode->_col == BLACK) //删除的是黑色结点
		{
			if (deleteNode->_left)
				deleteNode->_left->_col = BLACK;

			else if (deleteNode->_right) //待删除结点有一个红色的右孩子(不可能是黑色)
			{
				deleteNode->_right->_col = BLACK;
			}
			else //待删除结点的左右均为空
			{
				while (deleteNode != _root)
				{
					if (deleteNode == deleteParent->_left)
					{
						Node* brother = deleteParent->_right;
						//情况一:brother为红色
						if (brother->_col == RED)
						{
							deleteParent->_col = RED;
							brother->_col = BLACK;
							RotateL(deleteParent);

							brother = deleteParent->_right; //更新brother
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (deleteParent->_col == RED)
							{
								deleteParent->_col = BLACK;
								break;
							}

							deleteNode = deleteParent;
							deleteParent = deleteNode->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空
							if ((brother->_right == nullptr) || (brother->_right->_col == BLACK))
							{
								brother->_left->_col = BLACK;
								brother->_col = RED;
								RotateR(brother);

								brother = deleteParent->_right; //更新brother
							}
							//情况四:brother为黑色,且其右孩子是红色结点
							brother->_col = deleteParent->_col;
							deleteParent->_col = BLACK;
							brother->_right->_col = BLACK;
							RotateL(deleteParent);
							break;
						}
					}
					else  //待删除结点是其父结点的左孩子
					{
						Node* brother = deleteParent->_left;
						//情况一:brother为红色
						if (brother->_col == RED)
						{
							deleteParent->_col = RED;
							brother->_col = BLACK;
							RotateR(deleteParent);
							//需要继续处理
							brother = deleteParent->_left;
						}
						//情况二:brother为黑色,且其左右孩子都是黑色结点或为空
						if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK)))
						{
							brother->_col = RED;
							if (deleteParent->_col == RED)
							{
								deleteParent->_col = BLACK;
								break;
							}

							deleteNode = deleteParent;
							deleteParent = deleteNode->_parent;
						}
						else
						{
							//情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空
							if ((brother->_left == nullptr) || (brother->_left->_col == BLACK))
							{
								brother->_right->_col = BLACK;
								brother->_col = RED;
								RotateL(brother);

								brother = deleteParent->_left;
							}
							//情况四:brother为黑色,且其左孩子是红色结点
							brother->_col = deleteParent->_col;
							deleteParent->_col = BLACK;
							brother->_left->_col = BLACK;
							RotateR(deleteParent);
							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* _root;
};

map完整代码

template<class K,class V>
class map
{
	struct MapKeyOfT
	{
		const K& operator()(const pair<const K, V>& kv)
		{
			return kv.first;
		}
	};

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

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

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

	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& k)
	{
		return _t.erase();
	}

	iterator find(const K& k)
	{
		return _t.find();
	}
private:
	RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};

set完整代码

template<class K>
class set
{
	struct SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	typedef typename RBTree<K, K, SetKeyOfT>::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& k)
	{
		return _t.insert(k);
	}

	void erase(const K& k)
	{
		return _t.erase(k);
	}

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

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

说明: 我们对 map 和 set 进行模拟实现只是为了让我们更加了解它们的框架和红黑树的底层实现原理。

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

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

相关文章

音视频开发风潮 | 聊聊为啥从事音视频开发

音视频开发风潮 聊聊为啥从事音视频技术&#xff0c;未来会很赚钱 核心竞争力&#xff1a;音视频是程序界的皇冠&#xff0c;掌握音视频意味着拿到通往未来的船票&#xff0c;不用担心会被其他人替代。因为音视频是有一定门槛的&#xff0c;所以也是与其他人拉开差距的分水岭…

基于Linux的Spark安装与环境配置

文章目录基于Linux的Spark安装与环境配置1、Hadoop测试1.1 启动hadoop1.2 再次启动hadoop1.3 查看是否成功2、Scala安装与配置2.1 下载scala2.2 解压并重命名2.3 配置环境2.4 测试3、Spark安装与配置3.1 下载Spark3.2 解压并重命名3.3 配置环境3.4 配置spark-env.sh3.5 配置sla…

RabbitMQ中VirtualHost相关设置、SpringBoot中集成常见问题总结

wshanshi&#xff1a;总结记录…便于回顾… 一、VirtualHost相关设置 1.1、创建VirtualHost rabbitmqctl add_vhost 虚拟服务器名称如&#xff1a;rabbitmqctl add_vhost test 1.2、用户、权限相关设置 1.2.1、创建用户 rabbitmqctl add_user 用户名 密码如&#xff1a;ra…

Baklib经验分享 | 一些搭建帮助中心的攻略

作为一家企业&#xff0c;既要为用户提供优质的产品&#xff0c;又要为用户提供良好的售后服务。 很多企业在建立网站的时候可能低估了帮助中心的价值&#xff0c;但是实际上&#xff0c;一家好的在线帮助中心可以帮助企业留住用户&#xff0c;而且用户还可以通过自助查询来解…

【Redis系列】Linux查看Redis版本信息

目录 一、详细介绍 1.redis-server -v 1.1 进入容器 1.2 查询redis-server 目录 1.3 进入对应目录 1.4 执行命令 2.redis-cli -v 1.1 进入容器 1.2 查询redis-cli 目录 1.3 进入对应目录 1.4 执行命令 一般是有两种方式 redis-server -vredis-cli -v一、详细介绍 1.…

【数据库系列】如何查看MongoDB版本信息

目录 一、方法介绍 1 mongo --version 2 使用mongod 3 db.version() 二、总结 三种方式可以查看&#xff0c;依次如下&#xff1a; 一、方法介绍 首先要找到mongo的路径&#xff0c;如果不知道在哪&#xff0c;可以输入 whereis mongo 如下图&#xff0c;找到对应的目录…

VMWare安装CentOS7操作系统的虚拟机

【原文链接】VMWare安装CentOS7操作系统的虚拟机 &#xff08;1&#xff09;可以从 清华大学镜像源 下载centos7的镜像&#xff0c;比如下载如下这个版本 &#xff08;2&#xff09;打开VMWare后&#xff0c;点击【创建虚拟机】 &#xff08;3&#xff09;选择【自定义高级…

OAuth 2.0 (第三方登录)前端流程实现

目录 一、OAuth是什么 二、OAuth 实现&#xff0c;前端需要做什么 &#xff08;一&#xff09;数据收集表单 &#xff08;二&#xff09;获取后端返回值 &#xff08;三&#xff09;重定向地址及后续处理 三、项目地址 一、OAuth是什么 OAuth就是让"客户端"安全…

vue-router入门学习3

命名路由 使用命名路由实现声明式导航 要在路由规则里加上name属性&#xff0c;相对于给这个地址取别名&#xff0c;当地址非常长时可以充分体现它的好用之处。 这里的to属性前面要加个冒号噢 调用push函数期间指定一个配置对象&#xff0c;name是要跳转到的路由规则、params是…

真题集P127---2018年真题

真题集P127---2018年真题第一题思路辗转相除法代码第三题思路代码第六题思路代码第一题 思路 就是在考学生&#xff0c;如何快速求解最大公约数问题 <1>从mn中选较小的数开始向下枚举&#xff0c;一直到2为止&#xff0c;第一个能同时整除m,n的即为所求(暴力枚举不推荐)…

Java这些最基础的知识,你还记得多少?

目录 【一】前言 【二】基础数据类型 【三】修饰符 【四】集合 【五】总结 【一】前言 作为一名CS本专业的程序员来说&#xff0c;大学学过的计算机课程&#xff0c;如&#xff1a;编程语言基本语法、数据结构、算法、计算机网络、计算机组成原理、数据库、汇编都是计算机…

nodejs基于微信小程序的书籍销售系统--(ssm+uinapp+Mysql)

伴随着社会以及科学技术的发展,互联网已经渗透在人们的身边,网络慢慢的变成了人们的生活必不可少的一部分,随着互联网的飞速发展,系统这一名词已不陌生,越来越多的书店都会使用系统来定制一款属于自己个性化的系统。书籍销售系统采用nodejs技术, mysql数据库进行开发,实现了首页…

人脑能否重启?

1.重启是什么 “人脑能否重启”这个问题还不简单&#xff0c;人睡眠后清醒就是重启。 事实真的是如此简单吗&#xff1f;我们先不急着给出结论&#xff0c;前面提到“人睡眠后清醒就是重启”&#xff0c;这句话中有两概念&#xff1a; 1、睡眠和觉醒&#xff0c;这是两种人脑…

计算机算法与分析--算法实现题1

代码网上一抓一大把&#xff0c;所以我就不贴代码了&#xff0c;主要讲讲解题的思路&#xff0c;相信各位都能随便写出来。 1-1 统计数字问题 第一反应就是直接循环&#xff0c;然后每个数字进行统计。虽然很容易想到&#xff0c;但肯定会超时的宝贝&#xff01;用心找到位数…

MySQL梳理

MySQL数据库总结篇_许小许520的博客-CSDN博客_mysql数据库文章 MySQL简介 MySQL安装 MySQL建库/表/记录 MySQL基本操作语句 MySQL字段类型&#xff0c;约束条件 int,float...pk,unique.. MySQL单/多表关系 四个关系 MySQL单/多表查询 MySQL查询关键字 where&#xff0c;havi…

学编程:Python入门考级必备[5]

海龟画图(5) 妙手丹青 一、初始化 二、坐标与角度 三、画圆与点与技巧 四、填充颜色与圆内内切多边形 炼 海龟画图 一、初始化 英文:turtle 中文:海龟 海龟能在一个画布上游走&#xff0c;游走的轨迹就形成了绘制的图形。 1.怎么在 Python 编程中找到海龟呢&#xff1f; …

从0到1实现python基于RPC协议的接口自动化测试

01、什么是RPC RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用协议是一个用于建立适当框架的协议。从本质上讲&#xff0c;它使一台机器上的程序能够调用另一台机器上的子程序&#xff0c;而不会意识到它是远程的。 RPC 是一种软件通信协议&#xff0c;一个程…

安装HBase集群

安装HBase集群 1 软件环境 注&#xff1a;我的Hadoop环境已经安装完成。 1.1 版本选择 版本的兼容问题很重要&#xff01;&#xff01; 软件环境版本号备注CentOS77.6JDK1.8.0Hadoop3.1.3hadoop-3.1.3.tar.gzZooKeeper3.6.3apache-zookeeper-3.6.3-bin.tar.gzHBase2.4.15h…

宝塔centos7安装Conda

前言&#xff1a;最近学习了python&#xff0c;主要原因是公司主营百度相关业务&#xff0c;接触了一下paddle Ai开发套件&#xff0c;其中paddlehub安装一直有问题&#xff0c;windows环境也好还是liunx环境也好一直安装不了最新版本&#xff0c;应该是某个库版本问题&#xf…

shell脚本编程

shell介绍 Shell的本意是“壳”的意思,它是相对于操作系统的“壳”。外界的操作不能直接调用操作系统内核,需要通过Shell脚本进行调用。 Shell本质是一个解释器程序,用于接受应用程序/用户命令,然后调用操作系统内核。Shell还是一个功能相当强大的编程语言,易编写,易调…