利用红黑树封装map,和set,实现主要功能

如果不知道红黑树是什么的时候可以去看看这个红黑树

思路

首先我们可以把封装分为两个层面理解,上层代码就是set,和map,底层就是红黑树
就相当于根据红黑树上面套了两个map,set的壳子,像下面这张图一样
在这里插入图片描述
对于map和set,map里面存放的是pair一个键值对,而set就只是一个key,一般的想法是实现两个红黑树,一个里面的节点放key一个放pair,那么这样就太冗余了,那么我们只用实现一个红黑树就行了,我们利用模板就可以很轻松的解决冗余的问题
下面就来看看怎么具体的实现的

创建set,map

这里的set和map我们统称为myset,mymap
我们首先根据红黑树的基础把set,和map的架子搭起来
先来实现基本的插入功能

# include"RBtree.h"
namespace mymap
{
   
	template<class key, class value>
	class mymap
	{
   
	public:
		struct k_of_t_map
		{
   
			const key& operator()(const pair<key,value>& input_map)
			{
   
				return input_map.first;
			}
		};
		bool Insert(const pair<key, value>& input_map)
		{
   
			return _t.insert(input_map);
		}
	private:
		RBtree<key, pair< const key, value>, k_of_t_map> _t;
	};
}

我们在插入的时候是根据搜索树的规则进行插入的,搜索树肯定是要比较大小的,我们的pair他原生是支持比较大小的,但是库里面的比较大小是根据first和second来进行比较的,我们这里肯定不是这样的,我们是想两个pair不同的first进行比较大小的,所以库里面的比较无法满足我们的需求
这里我们的解决方法是利用仿函数

struct k_of_t_map
		{
   
			const key& operator()(const pair<key,value>& input_map)
			{
   
				return input_map.first;
			}
		};

我们通过实现一个内部类对operator()的重载,实现类似于函数()的功能,就能像函数一样使用,去实现我们想要的功能,这里我们通过operator()我们直接返回input_map.first,就得到了我们想要的pair的first也就是用来比较大小的key

同样set也是一样的道理
这里的set其实有点冗余了,是为了配合map而创建的,他本身就不需要都行

# include"RBtree.h"
namespace myset
{
   
	template<class key>
	class myset
	{
   
	public:

		struct k_of_t_set
		{
   
			const key& operator()(const key& input_key)
			{
   
				return input_key;
			}
		};
		bool Insert(const key& input_key)
		{
   
			return _t.insert(input_key);
		}
	private:
		RBtree< key,const key,k_of_t_set> _t;
	};
}

有了上面的架子之后,我们红黑树的插入功能里面的比较大小的方法,也需要改一下,改成在比较大小的时候去调用mymap,myset里面的内部类里面的成员函数。

所以我们在创建mymap,myset的成员变量时,就要根据RBtree在加上自身的性质来创建,这里mymap和myset最核心的差别就是一个数据类型,另一个是比较大小的逻辑不同,所以我们在RBtree的模板参数就要变成

template<class key, class T, class k_of_t>

多提供一个k_of_t的模板参数 这个就是用来设置比较大小的方法用的
所以我们对于的mymap,myset就要变成

RBtree<key, pair< const key, value>, k_of_t_map> _t;
RBtree< key,const key,k_of_t_set> _t;

然后再插入的比较大小的逻辑也要套用这个内部类的成员函数
首先通过k_of_t来创建一个 kot的对象
然后再根据这个对象去调用相应的成员函数
在这里插入图片描述
关于mymap,myset的成员变量的初始化
其实我们根据代码我们就可以发现mymap,myset的成员函数的初始化其实就是去复用RBtree的初始化
就像一个映射一样,**假设初始化mymap把mymap里面的模板参数传过去,然后再去初始化RBtree的_root。**把相应的数据类型比较大小的方法替换的mymap的比较方法
myset也是同理。
在这里插入图片描述
有了以上的步骤之后我们就可以实现mymap和myset的插入了

map set的迭代器

下面我们讲讲迭代器的实现
我们先来看看迭代器的构造

template<class T,class ref,class ptr>
//迭代器就相当于一个节点的指针
struct RBtreeIterator//默认公有
{
   
	typedef RBtreenode<T> Node;
	typedef RBtreeIterator<T, ref, ptr> self;

	Node* _it_node;
	RBtreeIterator (Node* input_node, Node* root)
		:_it_node(input_node)
	{
   }

这里我们传了三个模板参数一个是数据类型一个是引用,最后一个是指针
这样的目的是为了方便我们想要引用的时候用引用,想要指针的时候用指针
这里我们的成员变量是一个RBtreenode类型,我们需要构造一个节点,把他当作一个指针,做为我们迭代器遍历容器的一个媒介迭代器就相当于一个节点的指针
了解了上面的结构之后
下面我们来看看迭代器的operator++和–这个是迭代器的核心,也是比较难的

operator++

先来看看代码

self operator++()
{
   
	if (_it_node->_right != nullptr)//找右最小节点
	{
   
		Node* right_min = this->_it_node->_right;
		while (right_min->_left != nullptr)
		{
   
			right_min = right_min->_left;
		}
		_it_node = right_min;
	}
	else//右边访问完,代表以他为基本的左子树右子树都完了,就要向上走
	{
   
		Node* cur = _it_node;
		Node* parent = cur->_pre_node;
		//这里要找的是相对于儿子的父亲,父亲的右节点不是孩子的节点
		//如果一直是右节点就代表当前节点的左右子树都完了
		//如果找到父亲的左节点是孩子,就代表父亲还有右节点没访问完。
		while (parent != nullptr && parent->_right == cur)
		{
   
			cur = parent;
			parent = cur->_pre_node;
		}
		_it_node = parent;
	}
	return *this;
}

这里我们需要和红黑树的性质一起来说一下,我们知道红黑树的中序遍历出来是一个从小到大的有序序列
我们的++也是利用这个思想来的,但是中序遍历是一个看全局思想来的,但我们这里是要看局部,只考虑当前中序局部要访问的下⼀个结点。可以看成一个局部的左根右
为什么不能直接用中序遍历来写红黑树的迭代器
那是因为
随机访问需求 迭代器通常需要支持随机访问操作,这意味着可以在常数时间内访问任意一个元素。而中序遍历是线性的,不支持随机访问。
也不能更加灵活的访问容器,每次都要从根开始
我们现在来具体说一下代码

if (_it_node->_right != nullptr)//找右最小节点
	{
   
		Node* right_min = this->_it_node->_right;
		while (right_min->_left != nullptr)
		{
   
			right_min = right_min->_left;
		}
		_it_node = right_min;
	}

这里是找除开当前节点,如果当前节点的右边不为空,我们就要一直找右边的最小节点(也就是最左节点)
为什么要这样找,我们可以联系下面的这个beign()这个函数来看看

typedef RBtreenode<T> node;
typedef RBtreeIterator<T, T&, T*> Iterator;
typedef RBtreeIterator<T, const T&, const T*> ConstIterator;
Iterator begin()
{
   
	node* cur = _root;
	while (cur != nullptr && cur->_left != nullptr)
	{
   
		cur = cur->_left;
	}
	return Iterator(cur);
}

这个begin()返回的是一个迭代器类型,他是一直找红黑树的最小节点,找到之后用最小节点去构造一个迭代器类型,当找到最小节点的时候就代表以这个节点为基准他的左子树已经访问完了然后要去看看右子树,我们可以画图看看
在这里插入图片描述
然后我们来看看下面的代码

	else//右边访问完,代表以他为基本的左子树右子树都完了,就要向上走
	{
   
		Node* cur = _it_node;
		Node* parent = cur->_pre_node;
		//这里要找的是相对于儿子的父亲,父亲的右节点不是孩子的节点
		//如果一直是右节点就代表当前节点的左右子树都完了
		//如果找到父亲的左节点是孩子,就代表父亲还有右节点没访问完。
		while (parent != nullptr && parent->_right == cur)
		{
   
			cur = parent;
			parent = cur->_pre_node;
		}
		_it_node = parent;
	}
	retur
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值