利用红黑树封装map和set

news2024/11/17 13:57:58

目录

  • 一、正向迭代器
    • 1.1 operator++
    • 1.2 operator--
    • 1.3 参考代码
  • 二、反向迭代器
  • 三、封装set
  • 四、封装map
  • 五、底层红黑树的实现

一、正向迭代器

我们之前vector,list这些都是容器的迭代器都是简单的指针++或者_node=_node->next这样的,那是因为它们要么是连续的空间,要么是一个节点中存放着下一个节点的地址,但是我们这里的红黑树的迭代器就没有那么简单了,因为这里的红黑树的各个节点既不是连续的内存,也没有在节点内存放着下一个节点的地址,所以红黑树的迭代器的++和–操作该如何完成呢?

1.1 operator++

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.2 operator–

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3 参考代码

	enum Colour
	{
		RED,
		BLACK
	};

	//当RBTree封装set的时候T是K类型
	//当RBTree封装map的时候T是pair<K,V>类型
	//这样设计RBTreeNode就可以让T接受什么类型
	//就是什么类型,符合泛型编程
	template <class T>
	struct RBTreeNode
	{
	public:
		//红黑树存放的值
		T _data;

		//节点的三叉链指针
		RBTreeNode<T>* _left;
		RBTreeNode<T>* _right;
		RBTreeNode<T>* _parent;

		//节点的颜色
		Colour _col;

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

	};

	template <class T,class Ref,class Ptr>
	struct __RBTree_iterator
	{
		//由于这里的iterator的传的参数是T,T&,T*,跟Self是不一样的,
		// 所以无论如何这里的iterator就是普通迭代器
		typedef __RBTree_iterator<T, T&, T*> iterator;
		typedef __RBTree_iterator<T, Ref, Ptr> Self;
		typedef RBTreeNode<T> Node;

	public:

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

		//当我们是实例化出const迭代器的时候,这里是一个构造函数,是利用普通迭代器构造一个const迭代器的构造函数
		//当我们是实例化出一个普通迭代器的时候,这里是一个拷贝构造函数,即利用普通迭代器拷贝构造出一个普通迭代器
		//这个函数必须提供,因为后面封装set必须要用到这个,否则会报错
		__RBTree_iterator(const iterator& it)
			:_node(it._node)
		{}
	
	public:
		Ref operator*()
		{
			return _node->_data;
		}

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

		Self& operator++()
		{
			//找下一个节点
			Node* subR = _node->_right;

			//如果右子树不为空,那么下一个节点就是右子树的最左节点
			if (subR != nullptr)
			{
				Node* leftmost = subR;
				while (leftmost->_left)
				{
					leftmost = leftmost->_left;
				}
				//最后把_node更新成右子树的最左节点
				_node = leftmost;
			}
			else
			{
				//如果右子树为空,说明我的左子树访问完了,我也访问完了,那么下一个是访问我的父节点吗?
				//其实不是的如果我是父亲的右孩子节点,那么我访问完了,但是因为我是父亲的右孩子节点,
				//所以我访问完了,并且我的父亲也访问完了,所以这时的下一个需要访问的节点并不是我的
				//父节点,而是要沿祖先路径查找我是父亲的左孩子的那个父亲,这个父节点才是我们下一个
				//需要访问的节点,因为我是父亲的左孩子,那么我访问完了,下一个就是访问我的父节点了
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent)
				{
					if (cur == parent->_left)
					{
						break;
					}
					else
					{
						//找我是父亲左孩子的那个父亲
						cur = parent;
						parent = parent->_parent;
					}
				}

				//这个父节点就是我们下一个需要访问的节点,如果parent是nullptr,那么
				//说明找到根节点都没有找到我是父亲左孩子的那个父亲,说明这棵树已经访问
				//完了,我们是用nullptr作为迭代器的end的
				_node = parent;
			}

			return *this;
		}

		//找上一个节点
		Self& operator--()
		{
			Node* subL = _node->_left;
			//如果左子树存在,那么当前节点的上一个节点就是左子树的最右节点
			if (subL != nullptr)
			{
				/*找左子树的左右节点*/
				Node* rightmost = subL;
				while (rightmost->_right)
				{
					rightmost = rightmost->_right;
				}
				_node = rightmost;
			}
			//如果左子树为nullptr,那么沿祖先路径查找我是父亲的右孩子的那个父亲,
			//因为我是父亲的右孩子,那么我的前一个节点就是这个父亲
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent)
				{
					if (cur == parent->_right)
					{
						break;
					}
					else
					{
						cur = parent;
						parent = parent->_parent;
					}
				}

				_node = parent;
			}

			return *this;
		}

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

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

	public:
		Node* _node;
	};

二、反向迭代器

	//反向迭代器是使用了适配器模式,利用已有的正向迭代器适配出来的
	//传参时传的是正向迭代器Itreator,然后利用正向迭代器的接口封装
	//出反向迭代器的接口
	template <class Iterator,class Ref,class Ptr>
	struct __RBTree_reverse_iterator
	{
		typedef __RBTree_reverse_iterator<Iterator, Ref, Ptr> Self;
	public:
		//构造函数,利用正向迭代器构造出一个反向迭代器
		__RBTree_reverse_iterator(const Iterator& it)
			:_it(it)
		{}

		//通过运算符重载得到我们想要的效果,因为反向迭代器的++就是正向迭代器的--,
		//所以直接调用正向迭代器的--接口即可完成反向迭代器的++操作
		Self& operator++()
		{
			--_it;
			return *this;
		}
		//同理,反向迭代器的--就是正向迭代器的++,所以直接调用正向迭代器的++即可
		Self& operator--()
		{
			++_it;
			return *this;
		}
		bool operator!=(const Self& it)
		{
			return _it != it._it;
		}
		Ref operator*()
		{
			return *_it;
		}
		Ptr operator->()
		{
			return &(operator*());
		}
	private:
		Iterator _it;
	};

三、封装set

在这里插入图片描述

#pragma once

#include "RBTree.h"

namespace kb
{
	template <class K>
	class MySet
	{
		//仿函数,用于取出红黑树的节点的key值,方便在插入节点的时候作大小比较,
		//如果是set的话取出来的就是key本身
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

		//对于set来说,set的模型和map的模型是一样的,都是key-value模型,set的key-value
		//对应的是key-key,map的key-value对应的是key-pair<K,V>
		//对于set我们知道,无论是set的key还是set的value都是不允许修改的,所以要把通过迭代器
		//修改的行为直接禁掉,所以对于set来说,无论是iterator还是const_iterator的底层都是
		// 用红黑树中的const_iterator,所以我们在使用set的iterator的时候看似使用的是普通
		//迭代器,实际上底层也是const迭代器,这样做就能从根本上杜绝通过普通迭代器修改set的key
		//或者value的行为了,这个库里面的实现方式,这个做法是真的绝
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		//反向迭代器也是按照正向迭代器那样设计来达到不让别人通过普通迭代器修改set的key或者value值的
		typedef typename RBTree<K, K, SetKeyOfT>::const_reverse_iterator reverse_iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::const_reverse_iterator const_reverse_iterator;

	public:

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

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

		const_iterator cbegin()const
		{
			return _set.begin();
		}

		const_iterator cend()const
		{
			return _set.end();
		}

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

		reverse_iterator rbegin() const
		{
			return _set.rbegin();
		}

		reverse_iterator rend()
		{
			return _set.rend();
		}
		
		reverse_iterator rend() const
		{
			return _set.rend();
		}

		//这个返回值的pair中的iterator是27行typedef出来的iterator,所以这个iterator看起来是
		//普通迭代器,实际上是const迭代器
		pair<iterator,bool> insert(const K& x)
		{
			//重点理解
			//调用Insert函数返回的pair中的first是普通迭代器,而这里的作为返回值的pair中的
			// iterator实际上是const_iterator,所以无法从普通的迭代器直接转化成const_iterator,
			// 所以需要先用一个临时的pair接受Insert的返回值,这个临时的pair的第一个参数必须要显式
			// 地指明是红黑树中的普通迭代器,然后再利用临时的pair中的第一个参数的这个普通迭代器构造
			// 一个const迭代器,注意需要在iterator中提供一个构造const迭代器的构造函数,否则会报错
			pair<typename RBTree<K, K, SetKeyOfT>::iterator,bool> ret = _set.Insert(x);
			return pair<iterator, bool>(ret.first, ret.second);


			//也可以像这样子写,因为我们已经提供了一个用普通迭代器构造const迭代器的构造函数,但是
			// //不建议这样子写,因为这样子写我们对这个需要注意的细节就隐藏了,降低了可读性
			//return _set.Insert(x);

		}

		void clear()
		{
			_set.clear();
		}


	private:
		//对于set,key和value都是K类型,并且需要把比较的仿函数也作为模板参数传过去
		RBTree<K, K, SetKeyOfT> _set;
	};
}

四、封装map

#pragma once


#include "RBTree.h"

namespace kb
{
	template <class K,class V>
	class MyMap
	{
		//仿函数,用于取出红黑树的节点的key值,方便在插入节点的时候作大小比较,
		//如果是map的话取出来的就是pair中的key值
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

		//对于map,因为map的key值是不能被修改的,但是它的value值是允许被修改的,所以这里的
		//迭代器就不能像set那里的迭代器那样设计了,因为如果像那样设计的话,无论是key还是value
		//都没办法进行修改了,那需要怎么控制住key不能被修改,而value要能被修改呢?库里面的操作
		//也很牛啊,它是直接在传pair的key时直接传一个const K构造pair,而V是普通的类型,这样设计
		//就能使pair中的key不能被修改,而value能被修改。
		//const迭代器就是pair的K和V都传const,这样pair的K,V也就都不能被修改了
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, const V>, MapKeyOfT>::const_iterator const_iterator;

		//反向迭代器也是同样的设计
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::reverse_iterator reverse_iterator;
		typedef typename RBTree<K, pair<const K, const V>, MapKeyOfT>::const_reverse_iterator const_reverse_iterator;


	public:

		//这里的插入函数的返回值的pair中的iterator和Insert的返回值的pair的iterator
		//都是相同的类型,都是普通迭代器,不存在类型转换,所以就无需像set中的insert那样设计
		pair<iterator,bool> insert(const pair<K, V>& x)
		{
			return _map.Insert(x);
		}

		//[]的运算符的重载就是利用insert的返回值实现的
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _map.Insert(make_pair(key, V()));
			return ret.first->second;
		}

		void clear()
		{
			_map.clear();
		}

		
		//封装迭代器
		iterator begin()
		{
			return _map.begin();
		}

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

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

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

		const_reverse_iterator rbegin() const
		{
			return _map.end();
		}

		const_reverse_iterator rend() const
		{
			return _map.begin();
		}

	private:
		//直接从这里传参实例化这棵树的时候把pair的K设置为const,这样
		//对于map的key就不能被修改了,而value依然能够被修改
		RBTree<K, pair<const K,V>, MapKeyOfT> _map;
	};
}


五、底层红黑树的实现

#pragma once


#include <iostream>
using namespace std;
#include <assert.h>

namespace kb
{
	enum Colour
	{
		RED,
		BLACK
	};

	//当RBTree封装set的时候T是K类型
	//当RBTree封装map的时候T是pair<K,V>类型
	//这样设计RBTreeNode就可以让T接受什么类型
	//就是什么类型,符合泛型编程
	template <class T>
	struct RBTreeNode
	{
	public:
		//红黑树存放的值
		T _data;

		//节点的三叉链指针
		RBTreeNode<T>* _left;
		RBTreeNode<T>* _right;
		RBTreeNode<T>* _parent;

		//节点的颜色
		Colour _col;

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

	};

	template <class T,class Ref,class Ptr>
	struct __RBTree_iterator
	{
		//由于这里的iterator的传的参数是T,T&,T*,跟Self是不一样的,
		// 所以无论如何这里的iterator就是普通迭代器
		typedef __RBTree_iterator<T, T&, T*> iterator;
		typedef __RBTree_iterator<T, Ref, Ptr> Self;
		typedef RBTreeNode<T> Node;

	public:

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

		//当我们是实例化出const迭代器的时候,这里是一个构造函数,是利用普通迭代器构造一个const迭代器的构造函数
		//当我们是实例化出一个普通迭代器的时候,这里是一个拷贝构造函数,即利用普通迭代器拷贝构造出一个普通迭代器
		//这个函数必须提供,因为后面封装set必须要用到这个,否则会报错
		__RBTree_iterator(const iterator& it)
			:_node(it._node)
		{}
	
	public:
		Ref operator*()
		{
			return _node->_data;
		}

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

		Self& operator++()
		{
			//找下一个节点
			Node* subR = _node->_right;

			//如果右子树不为空,那么下一个节点就是右子树的最左节点
			if (subR != nullptr)
			{
				Node* leftmost = subR;
				while (leftmost->_left)
				{
					leftmost = leftmost->_left;
				}
				//最后把_node更新成右子树的最左节点
				_node = leftmost;
			}
			else
			{
				//如果右子树为空,说明我的左子树访问完了,我也访问完了,那么下一个是访问我的父节点吗?
				//其实不是的如果我是父亲的右孩子节点,那么我访问完了,但是因为我是父亲的右孩子节点,
				//所以我访问完了,并且我的父亲也访问完了,所以这时的下一个需要访问的节点并不是我的
				//父节点,而是要沿祖先路径查找我是父亲的左孩子的那个父亲,这个父节点才是我们下一个
				//需要访问的节点,因为我是父亲的左孩子,那么我访问完了,下一个就是访问我的父节点了
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent)
				{
					if (cur == parent->_left)
					{
						break;
					}
					else
					{
						//找我是父亲左孩子的那个父亲
						cur = parent;
						parent = parent->_parent;
					}
				}

				//这个父节点就是我们下一个需要访问的节点,如果parent是nullptr,那么
				//说明找到根节点都没有找到我是父亲左孩子的那个父亲,说明这棵树已经访问
				//完了,我们是用nullptr作为迭代器的end的
				_node = parent;
			}

			return *this;
		}

		//找上一个节点
		Self& operator--()
		{
			Node* subL = _node->_left;
			//如果左子树存在,那么当前节点的上一个节点就是左子树的最右节点
			if (subL != nullptr)
			{
				/*找左子树的左右节点*/
				Node* rightmost = subL;
				while (rightmost->_right)
				{
					rightmost = rightmost->_right;
				}
				_node = rightmost;
			}
			//如果左子树为nullptr,那么沿祖先路径查找我是父亲的右孩子的那个父亲,
			//因为我是父亲的右孩子,那么我的前一个节点就是这个父亲
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent)
				{
					if (cur == parent->_right)
					{
						break;
					}
					else
					{
						cur = parent;
						parent = parent->_parent;
					}
				}

				_node = parent;
			}

			return *this;
		}

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

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

	public:
		Node* _node;
	};

	//反向迭代器是使用了适配器模式,利用已有的正向迭代器适配出来的
	//传参时传的是正向迭代器Itreator,然后利用正向迭代器的接口封装
	//出反向迭代器的接口
	template <class Iterator,class Ref,class Ptr>
	struct __RBTree_reverse_iterator
	{
		typedef __RBTree_reverse_iterator<Iterator, Ref, Ptr> Self;
	public:
		//构造函数,利用正向迭代器构造出一个反向迭代器
		__RBTree_reverse_iterator(const Iterator& it)
			:_it(it)
		{}

		//通过运算符重载得到我们想要的效果,因为反向迭代器的++就是正向迭代器的--,
		//所以直接调用正向迭代器的--接口即可完成反向迭代器的++操作
		Self& operator++()
		{
			--_it;
			return *this;
		}
		//同理,反向迭代器的--就是正向迭代器的++,所以直接调用正向迭代器的++即可
		Self& operator--()
		{
			++_it;
			return *this;
		}
		bool operator!=(const Self& it)
		{
			return _it != it._it;
		}
		Ref operator*()
		{
			return *_it;
		}
		Ptr operator->()
		{
			return &(operator*());
		}
	private:
		Iterator _it;
	};

	template <class K,class T,class KeyOfT>
	class RBTree
	{
		typedef RBTreeNode<T> Node;
	public:

		typedef __RBTree_iterator<T, T&, T*> iterator;		//正向普通迭代器
		typedef __RBTree_iterator<T, const T&, const T*> const_iterator;  //正向const迭代器

		typedef __RBTree_reverse_iterator<iterator, T, T*> reverse_iterator; //反向普通迭代器
		typedef __RBTree_reverse_iterator<const_iterator, const T, const T*> const_reverse_iterator;//反向const迭代器


	public:
		RBTree()
			:_root(nullptr)
		{}

		//拷贝构造
		RBTree(const RBTree& rbt)
			:_root(nullptr)
			,_num(rbt._num)
		{
			_root = Copy(rbt._root);
		}

		Node* Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* ret = new Node(root->_data);
			ret->_col = root->_col;
			ret->_left = Copy(root->_left);
			if (ret->_left)
			{
				ret->_left->_parent = ret;
			}
			ret->_right = Copy(root->_right);
			if (ret->_right)
			{
				ret->_right->_parent = ret;
			}

			return ret;
		}

		void Swap(Node*& tmp)
		{
			swap(_root, tmp);
		}

		//赋值重载
		RBTree& operator=(RBTree root)
		{
			Swap(root._root);
			return *this;
		}


		//析构函数
		~RBTree()
		{
			clear();
		}

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

		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;

			//如果是空树,那么就直接插入一个黑色节点做根即可
			//注意要改颜色,因为节点的颜色默认是红色的
			if (_root == nullptr)
			{
				_root = new Node(data);
				_root->_col = BLACK;
				return make_pair(iterator(_root), true);
			}

			//根据二叉搜索树的规则插入节点,同时记录父节点
			Node* parent = nullptr;
			Node* cur = _root;

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

			cur = new Node(data);
			Node* newNode = cur;
			if (kot(data) < kot(parent->_data))
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			else if (kot(data) > kot(parent->_data))
			{
				parent->_right = cur;
				cur->_parent = parent;
			}

			//走到这里已经插入完成,后面是检查新插入的节点有没有破坏红黑树的规则的逻辑

			//检查新插入的节点是否满足红黑树的规则,如果不满足就要进行变色/变色+旋转
			//因为新插入的cur节点的颜色一定是红色的,当cur的父节点存在并且为红色
			//时说明出现了连续的两个红色节点,这时需要进行变色/变色+旋转,如果父节点
			// 不存在或者存在且为黑色时就无需再处理了。
			// 为什么父节点有可能不存在?因为这是一个循环,循环更新往祖先处理可能到达根节点,
			//到了根节点就无需再处理了
			while (parent && parent->_col == RED)
			{
				Node* grandfather = parent->_parent;

				if (parent == grandfather->_left)
				{
					Node* uncle = grandfather->_right;

					if (cur == parent->_left)
					{
						//             grandfather
						//     parent
						//cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;//这里曾经漏写了
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr||uncle->_col==BLACK)
						{
							//单纯的左边高,进行右单旋+变色
							RotateR(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;

							//旋转完之后无需再沿祖先路径处理
							break;
						}
					}
					else//cur == parent->_right
					{
						//              grandfather
						//       parent
						//              cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续往上调整
							cur = grandfather;
							parent = cur->_parent;//这里曾经忘记写
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//左右双旋
							RotateL(parent);
							RotateR(grandfather);
							//变色
							grandfather->_col = RED;
							cur->_col = BLACK;

							//旋转后就无需再沿祖先路径检查了,具体原因画图理解
							break;
						}

					}
				}
				else//(parent == grandfather->_right)
				{
					Node* uncle = grandfather->_left;
					if (cur == parent->_right)
					{
						//          grandfather
						//                         parent
						//                                    cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//单纯的右边高,进行左单旋+变色
							RotateL(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;

							//旋转+变色后就不需要再沿祖先路径检查了
							break;
						}
					}
					else//(cur == parent->_left)
					{
						//          grandfather
						//                          parent
						//          cur

						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;
							
							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//根据模型可知需要右左双旋+变色
							RotateR(parent);
							RotateL(grandfather);
							grandfather->_col = RED;
							cur->_col = BLACK;

							//旋转后就不需要再沿祖先路径检查了
							break;
						}
					}
				}
			}

			_num++;

			//最后记得把根节点的颜色改成黑色
			_root->_col = BLACK;
			return make_pair(iterator(newNode), true);
		}

		void clear()
		{
			Destroy(_root);
			_root = nullptr;
			_num = 0;
		}

		bool empty()
		{
			return _root == nullptr;
		}

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

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

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

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

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

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

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

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

		size_t Size()
		{
			return _num;
		}

		bool Isbalance()
		{
			return _Isbalance(_root);
		}

	private:

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

			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
			root = nullptr;
		}

		bool CheckColour(Node* root, int blackNum, int BenchMark)
		{
			//走到空树说明这条路径已经走完了,需要检查黑色节点的个数和基准值相不相等
			if (root == nullptr)
			{
				//如果不相等,证明不平衡,返回false
				if (blackNum != BenchMark)
				{
					return false;
				}
				//如果相等,则说明本条路径没有出事,还要检查其它路径
				return true;
			}

			//如果出现连续红色节点证明这棵树出问题了,返回false
			if (root->_col == RED && root->_parent && root->_parent->_col == RED)
			{
				return false;
			}

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

			//递归检查所有路径的颜色
			return CheckColour(root->_left, blackNum, BenchMark)
				&& CheckColour(root->_right, blackNum, BenchMark);

		}

		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);
		}
		
		//旋转的细节如果不清楚的话请看上一篇关于AVL树的旋转,红黑树的旋转和AVL树的旋转是一样的
		void RotateL(Node* parent)
		{
			assert(parent);
			Node* cur = parent->_right;
			Node* curleft = cur->_left;
			Node* parentParent = parent->_parent;

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

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

		}

		void RotateR(Node* parent)
		{
			assert(parent);
			Node* cur = parent->_left;
			Node* curright = cur->_right;
			Node* parentParent = parent->_parent;

			parent->_left = curright;
			cur->_right = parent;
			if (curright)
			{
				curright->_parent = parent;
			}
			parent->_parent = cur;

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

				cur->_parent = parentParent;
			}

		}

	private:
		Node* _root = nullptr;
		int _num = 0;//统计树的节点个数
	};

}

以上就是利用红黑树封装map和set的内容啦,因为map和set的接口太多了,这里只是把插入接口和迭代器封装出来,其它的接口就不一一封装了,感兴趣的老铁可以自己尝试一下封装其它有用的接口哟。以上就是今天想要跟大家分享的内容咯,你学会了吗?如果你感觉到有所收获,可以点点小心心,点点关注哦,后期还会持续更新C++的相关知识哦,我们下期见!!!!!!!!!!!

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

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

相关文章

数据可视化:揭示隐藏信息的强大工具

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 引言 数据可视化是将数…

WebGL透视投影

目录 透视投影 透视投影可视空间 可视空间构造效果图 Matrix4.setPerspective&#xff08;&#xff09; 三角形与可视化空间的相对位置 示例代码 代码详解 示例效果 投影矩阵的作用 透视投影矩阵对物体进行了两次变换 透视投影变换示意图 透视投影 在透视投影下&…

华为云云耀云服务器L实例评测|拉取创建canal镜像配置相关参数 搭建canal连接MySQL数据库 spring项目应用canal初步

前言 最近华为云云耀云服务器L实例上新&#xff0c;也搞了一台来玩&#xff0c;本篇博客介绍如何在华为云上部署canal的docker镜像&#xff0c;以及在spring项目中的初步应用。 其他相关的华为云云耀云服务器L实例评测文章列表如下&#xff1a; 初始化配置SSH连接 & 安装…

IntelliJ IDEA使用_常用快捷键(windows版)

文章目录 版本说明搜索操作层级关系查看光标选择代码定位代码操作Git操作编辑器操作 版本说明 当前的IntelliJ IDEA 的版本是2021.2.2&#xff08;下载IntelliJ IDEA&#xff09; ps&#xff1a;不同版本一些图标和设置位置可能会存在差异&#xff0c;但应该大部分都差不多。…

C++ PrimerPlus 复习 第四章 复合类型(上)

第一章 命令编译链接文件 make文件 第二章 进入c 第三章 处理数据 第四章 复合类型 &#xff08;上&#xff09; 文章目录 创建和使用数组&#xff1b;**声明语句中初始化数组元素。****使用大括号的初始化&#xff08;列表初始化&#xff09;** 字符串创建和使用C风格字符…

好用免费的链接转二维码

能把链接等转成二维码的形式 &#xff0c;并且是完全免费的 &#xff0c;超级好用&#xff1a;草料网址二维码生成器 https://cli.im/url?3f07d81d705e31db2dcde5ca2feeece8 测试了博客的链接转成了二维码 &#xff0c;很好用

(入门向)面向萌新的算法比赛入门指南

什么是算法 算法是指解决问题或完成特定任务的一系列明确指令或步骤集合。它是一个定义良好、逐步执行的操作序列&#xff0c;用于将输入转换为输出。算法可用于计算、数据处理、自动化控制、问题解决等各个领域。 算法通常由一系列简单的操作组成&#xff0c;这些操作可以是…

Java基于SpringBoot的逍遥大药房管理平台

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 大家好&#xff0c;我是程序员徐师兄、今天给大家谈谈基于android的app开发毕设题目&#xff0c;以及基于an…

GIS前端编程—视频展示

GIS前端编程—视频展示 视频展示1. 互联网公共地图服务开放平台2. 开源GIS服务平台 得益于互联网的快速发展&#xff0c;WebGIS发展迅猛&#xff0c;其开发工具与开发平台也呈现出百花齐放之势。目前&#xff0c;涌现出了大量的WebGIS二次开发产品。在互联网方向上&#xff0c;…

【Springboot】整合kafka

目录 安装zookeeperjdk安装zookeeper安装 安装kafka&#xff08;非集群&#xff09;springboot项目整合配置 安装zookeeper jdk安装 环境准备&#xff1a;CentOS7&#xff0c;jdk1.8 步骤如下&#xff1a; 下载自己需要的版本 这里使用的jdk1.8&#xff0c;获取链接如下 链接…

Maxwell 概述、安装、数据同步【一篇搞定】!

文章目录 什么是 Maxwell&#xff1f;Maxwell 输出格式Maxwell 工作原理Maxwell 安装Maxwell 历史数据同步Maxwell 增量数据同步 什么是 Maxwell&#xff1f; Maxwell 在大数据领域通常指的是一个用于数据同步和数据捕获的开源工具&#xff0c;由美国 Zendesk 开源&#xff0c…

千巡翼X1协调转弯功能

近年来&#xff0c;随着技术的飞速发展&#xff0c;无人机航测已成为现代测绘领域的一项重要应用。 无人机的出现极大地提高了航测的效率和精度&#xff0c;极大地减少了人力资源的投入。通过搭载各种高精度的航测仪器和传感器&#xff0c;无人机可以在短时间内完成大面积的航…

使用vscode以16进制方式查看bin文件内容

简介 方便对bin文件内容进行分析。 使用 VSCODE&#xff1a;插件下载 Hex Editor,下载完后使用vscode打开bin文件。 使用快捷键CtrlShiftP&#xff0c; 并在上方命令框输入>hex 选择 结果如下

微信小程序隐私授权

微信开发者平台新公告&#xff1a;2023年9月15之后&#xff0c;隐私协议将被启用&#xff0c;所以以后的小程序都要加上隐私协议的内容提示用户&#xff0c; 首先设置好隐私协议的内容&#xff0c;登录小程序的开发者后台&#xff0c;在设置--》服务内容声明--》用户隐私保护指…

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— JS基础(一)

&#xfecc;&#xfecc;&#xfecc;&#xfecc;♡‎&#xfecc;&#xfecc;&#xfecc;&#xfecc;♡‎‎&#xfecc;&#xfecc;&#xfecc;&#xfecc;♡‎&#xfecc;&#xfecc;&#xfecc;&#xfecc;♡&#xfecc;&#xfecc;&#xfecc;&#xfecc;…

OPC HDA扫盲

目录 1 基本概念 1.1 历史数据服务器类型 1.2 数据源 1.3 对象和接口概述 1.4 所需接口定义 1.5 可选接口定义 1.6 定义 1.7 边界值和时域 2 HDA聚合 2.1 生成间隔 2.2 数据类型 2.3 数据质量 3 聚合示例 3.1 示例数据 3.2 内插&#xff08;INTERPOLATIVE&#x…

构造与析构

在类的声明中&#xff0c;构造函数和析构函数是一类特殊的函数&#xff1a;由系统自动执行&#xff0c;在程序中不可显示地调用它们。 构造函数 作用&#xff1a;建立对象时对对象的数据成员进行初始化 特点&#xff1a; 构造函数是与类同名的特殊成员函数&#xff0c;没有…

Xamarin.Android实现App内版本更新

目录 1、具体的效果2、代码实现2.1 基本原理2.2 开发环境2.3 具体代码2.3.1 基本设置2.3.2 系统的权限授予2.3.3 进度条的layout文件2.3.4 核心的升级文件 3、代码下载4、知识点5、参考文献 1、具体的效果 有事需要在程序内集成自动更新的功能&#xff0c;网上找了下&#xff…

【ARM AMBA AXI 入门 11 - AXI 总线 AWCACHE 和 ARCACHE 介绍】

文章目录 1.1 AXI 传输事务属性1.1.1 slave type1.1.2 系统级缓存 1.2 Memory Attributes1.2.1 Bufferable&#xff0c;AxCACHE[0]1.2.2 Modifiable, AxCACHE[1]1.2.3 cache-allocate 1.3 Memory types 转自&#xff1a;https://zhuanlan.zhihu.com/p/148813963 如有侵权请联系…

学习记忆——英语篇

文章目录 英语字母形象起源右脑记忆单词的原则四大步骤第一步&#xff1a;摄取信息第二步&#xff1a;处理信息第三步&#xff1a;储存信息第四步&#xff1a;提取信息 训练例子字母形象训练 右脑记忆单词5大方法字源法编码法字母编码法字母组合编码法 拼音法全拼法拼音组合 熟…