目录
在stl中map和set的结构中,他们都使用一个红黑树进行封装。
由上图可知,set传给红黑树节点的两个模板参数都是key,而map传给的红黑树的第一个模板参数是key、第二个参数是pair,因此我们首先对上一篇文章中的红黑树进行改造。
一、改造红黑树
#pragma once
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 __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator<T, T&, T*> itertaor; 拷贝构造
// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
:_node(it._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
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)
{
// 1、左不为空,找左子树最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
// 2、左为空,孩子是父亲的右的那个祖先
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:
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
public:
typedef __RBTreeIterator<T, T&, T*> itertaor;
typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
itertaor begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return itertaor(cur);
}
itertaor end()
{
return itertaor(nullptr);
}
const_itertaor begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const_itertaor(cur);
}
const_itertaor end() const
{
return const_itertaor(nullptr);
}
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if (kot(cur->_data) > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
pair<itertaor, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(itertaor(_root), true);
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _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(itertaor(cur), false);
}
}
cur = new Node(data);
Node* newnode = cur;
if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// p u
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// g
// u p
// c
Node* uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(itertaor(newnode), true);;
}
bool IsBalance()
{
if (_root && _root->_col == RED)
{
cout << "根节点颜色是红色" << endl;
return false;
}
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
++benchmark;
cur = cur->_left;
}
// 连续红色节点
return _Check(_root, 0, benchmark);
}
int Height()
{
return _Height(_root);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (benchmark != blackNum)
{
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
{
++blackNum;
}
if (root->_col == RED
&& root->_parent
&& root->_parent->_col == RED)
{
cout << "存在连续的红色节点" << endl;
return false;
}
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppnode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subR;
}
else
{
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
}
private:
Node* _root = nullptr;
};
1、模板T
改造节点
我们需要将节点结构体RBTreeNode
中的数据成员_data
的类型改为模板参数T
。这样,每个节点就可以存储不同类型的数据,以适应map的节点存储pair结构,set的节点存储key。
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)
{}
};
2、提取节点中的key
在上述代码中,我们可以看到`RBTree`类的定义如下:
template<class K, class T, class KeyOfT>
class RBTree
{
// ...
};
其中,`K`是键的类型,`T`是值的类型,`KeyOfT`是一个辅助类也叫作仿函数,用于从值中提取键。在`set`中,键和值的类型相同,因此`KeyOfT`可以直接使用`K`。而在`map`中,键和值的类型不同,因此我们需要对`KeyOfT`进行修改。
对于`map`类,我们需要将`KeyOfT`修改为适应`pair<const K, V>`类型的辅助类。我们可以定义一个新的辅助类(仿函数)`MapKeyOfT`,其内部使用重载了函数调用运算符 operator()
,这个辅助类的作用是从`pair<const K, V>`类型的值中提取出键`K`。同时,我们将`RBTree`的第三个模板参数修改为`MapKeyOfT`。
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
对于`set`类,我们需要将`KeyOfT`修改为配合map使用的辅助类。我们可以定义一个仿函数`SetKeyOfT`, 通过重载operator()直接返回key。同时,我们将第三个模板参数修改为’SetKeyOfT'。
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
private:
RBTree<K, K, SetKeyOfT> _t;
};
3、迭代器类
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator<T, T&, T*> itertaor; 拷贝构造
// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
:_node(it._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++(){}
Self& operator--(){}
};
这段代码定义了一个模板类__RBTreeIterator
,它有三个模板参数:T
、Ref
和Ptr
。
T
:表示节点中存储的数据类型。Ref
:表示解引用操作符*
返回的引用类型。Ptr
:表示箭头操作符->
返回的指针类型。
这个迭代器类用于遍历红黑树,并提供了对节点数据的访问功能。
在这个迭代器类中,有一个成员变量Node* _node
,它指向红黑树中的一个节点。
- 构造函数
__RBTreeIterator(Node* node)
用于初始化迭代器对象,将_node
指针指向传入的节点。 - 拷贝构造函数
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
用于从普通迭代器构造const迭代器。它将传入迭代器的_node
指针赋值给当前迭代器的_node
成员变量。 - 重载解引用操作符
*
,使得可以通过迭代器访问节点的数据。它返回的是Ref
类型的引用,可以直接修改节点的数据。 - 重载箭头操作符
->
,使得可以通过迭代器访问节点的数据。它返回的是Ptr
类型的指针,可以通过指针访问节点的数据成员。 - 重载不等于操作符
!=
,用于比较两个迭代器是否不相等。它比较两个迭代器的_node
指针是否相等。 - 重载前缀递增操作符
++
,使得可以将迭代器指向下一个节点。如果当前节点有右子节点,则下一个节点是右子树的最左节点;否则,沿着到根的路径,找到第一个孩子是父节点左孩子的祖先节点。 - 重载前缀递减操作符
--
,使得可以将迭代器指向上一个节点。如果当前节点有左子节点,则上一个节点是左子树的最右节点;否则,沿着到根的路径,找到第一个孩子是父节点右孩子的祖先节点。
operator++
Self& operator++()
{
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
首先,函数检查当前节点的右子节点是否存在。如果存在,说明存在右子树,下一个节点就是右子树中的最左节点。因此,函数将迭代器指向右子树中的最左节点,并结束函数的执行。
如果当前节点的右子节点为空,说明不存在右子树。在这种情况下,函数需要沿着到根的路径向上查找,直到找到一个节点,该节点是其父节点的左子节点。这是因为在红黑树中,右子树为空的节点的下一个节点是其父节点。
函数使用两个指针变量 cur 和 parent 来进行查找。
- 开始时,将 cur 初始化为当前节点,将 parent 初始化为 cur 的父节点。
- 然后,函数进入一个循环,只要 parent 存在且 cur 是 parent 的右子节点,就继续向上查找。
- 在循环中,将 cur 更新为 parent,将 parent 更新为 cur 的父节点。
- 这样,函数就可以找到下一个节点,并将迭代器指向该节点。
最后,函数返回迭代器自身的引用,以支持链式操作。
operator--
Self& operator--()
{
if (_node->_left)
{
// 1、左不为空,找左子树最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
// 2、左为空,孩子是父亲的右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
首先,函数检查当前节点的左子节点是否存在。如果存在,说明存在左子树,前一个节点就是左子树中的最右节点。因此,函数将迭代器指向左子树中的最右节点,并结束函数的执行。
如果当前节点的左子节点为空,说明不存在左子树。在这种情况下,函数需要沿着到根的路径向上查找,直到找到一个节点,该节点是其父节点的右子节点。这是因为在红黑树中,左子树为空的节点的前一个节点是其父节点。
函数使用两个指针变量 cur
和 parent
来进行查找。
- 开始时,将
cur
初始化为当前节点,将parent
初始化为cur
的父节点。 - 然后,函数进入一个循环,只要
parent
存在且cur
是parent
的左子节点,就继续向上查找。 - 在循环中,将
cur
更新为parent
,将parent
更新为cur
的父节点。 - 这样,函数就可以找到前一个节点,并将迭代器指向该节点。
最后,函数返回迭代器自身的引用,以支持链式操作。
4、改造insert
1. 修改函数的参数类型为 `const T& data`,以接受要插入的数据。
返回类型 pair<iterator, bool>
是为了提供更多的信息给调用者,以便在插入操作后进行进一步的处理。
pair<iterator, bool> Insert(const T& data)
iterator
:表示插入节点的迭代器。通过返回迭代器,调用者可以方便地访问插入的节点,进行后续的操作,如修改节点的值、删除节点等。bool
:表示插入是否成功的标志。通过返回布尔值,调用者可以知道插入操作是否成功。如果插入成功,布尔值为true
;如果插入失败(节点已存在),布尔值为false
。
2. 将 `pair<K, V>` 替换为 `T`,因为现在的节点类型是 `RBTreeNode<T>`。
cur = new Node(data);
3. 在函数内部,将 `_kv` 替换为 `_data`,以匹配节点的数据成员名称。
if (cur->_data < data){
// ...
}
else if (cur->_data > data){
// ...
}
else{//查找失败
return make_pair(iterator(cur), false);
}
4. 新增的 KeyOfT kot;
是为了创建 KeyOfT
类型的对象 kot
,用于从节点数据类型 T
中提取键值。
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < kot(data))
{//..}
else if (kot(cur->_data) > kot(data))
{//..}
else
{//..}
}
5. 在修改后的 Insert
函数中,新增的 Node* newnode = cur;
是为了在返回 pair<iterator, bool>
时,将插入的节点的迭代器保存下来。
Node* newnode = cur;
- 在原始的代码中,返回的迭代器是通过
iterator(cur)
创建的,其中cur
是指向新插入节点的指针。然而,在后续的操作中,cur
的值可能会发生变化,因为在红黑树的调整过程中,节点的位置可能会发生改变。 - 为了确保返回的迭代器指向插入的节点,我们将
cur
的值保存在newnode
中。这样,在返回pair<iterator, bool>
时,我们使用iterator(newnode)
创建迭代器,确保迭代器指向插入的节点。 - 因此,
Node* newnode = cur;
的目的是为了保存插入节点的指针,以便在返回迭代器时使用。这样可以确保返回的迭代器指向正确的节点,而不受后续操作的影响。
6. 在函数的返回语句中,使用 `make_pair` 创建一个 `pair` 对象,其中包含插入的迭代器和插入是否成功的标志。
return make_pair(iterator(newnode), true);
5、红黑树迭代器
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __RBTreeIterator<T, T&, T*> itertaor;
typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
itertaor begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return itertaor(cur);
}
itertaor end()
{
return itertaor(nullptr);
}
const_itertaor begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const_itertaor(cur);
}
const_itertaor end() const
{
return const_itertaor(nullptr);
}
- 首先,我们定义了两个迭代器类型
iterator
和const_iterator
,分别对应可修改和只读的迭代器。这些迭代器是通过__RBTreeIterator
结构体模板实例化得到的。 - 然后,我们定义了
begin()
和end()
函数,分别用于返回红黑树的起始迭代器和结束迭代器。 - 在
begin()
函数中,我们从根节点开始,沿着左子节点的路径向下遍历,直到找到最左边的节点。这个节点就是红黑树中最小的节点,也是起始节点。我们将其作为参数传递给__RBTreeIterator
构造函数,创建一个迭代器对象,并将其返回。 - 在
end()
函数中,我们直接返回一个迭代器对象,其指针成员_node
设置为nullptr
,表示结束位置。 - 对于
const
重载的版本,逻辑与非const
版本相同,只是返回的是const_iterator
类型的迭代器对象。
6、 普通迭代器构造const迭代器
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator<T, T&, T*> itertaor; 拷贝构造
// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
:_node(it._node)
{}
}
const __RBTreeIterator<T, T&, T*>& it
部分没有显式的强制类型转换。它是一个常量引用,用于接收普通迭代器对象的引用作为参数,如果需要将普通迭代器对象转换为const迭代器对象,编译器会自动进行隐式类型转换。- 因为参数加上了
const
修饰符,这意味着它是一个常量引用。当使用普通迭代器对象调用构造函数时,编译器会进行隐式类型转换,将普通迭代器对象转换为const迭代器对象。
二、set
#pragma once
#include "RBTree.h"
namespace MySet
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_itertaor const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
- 在命名空间MySet中,定义了一个内部结构体SetKeyOfT,它是一个仿函数,用于从键中提取键值。在这个仿函数中,重载了函数调用操作符operator(),接受一个键key作为参数,并返回该键本身。
- 接下来是set类的定义。它使用了模板参数K,表示集合中元素的类型。在set类中,使用了RBTree类来实现红黑树的功能。RBTree的模板参数为K、K和SetKeyOfT,表示键的类型、值的类型和键提取仿函数的类型。
- set类中定义了两个迭代器类型:iterator和const_iterator,它们分别是RBTree<K, K, SetKeyOfT>::const_iterator的别名。这两个迭代器类型用于遍历集合中的元素。
- set类还提供了begin和end方法,用于返回集合的起始迭代器和结束迭代器。begin方法返回的是红黑树的起始迭代器,而end方法返回的是红黑树的结束迭代器。
- 此外,set类还提供了insert方法,用于向集合中插入元素。它接受一个键key作为参数,并返回一个pair对象,其中包含一个迭代器和一个布尔值。迭代器指向插入的元素,布尔值表示插入是否成功。
- 最后,set类中有一个私有成员变量_t,它是一个RBTree<K, K, SetKeyOfT>类型的对象,用于存储集合的元素。
三、map
#pragma once
#include "RBTree.h"
namespace MyMap
{
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>::itertaor iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));
return ret.first->second;
}
pair<iterator, bool> insert(const pair<const K, V>& kv)
{
return _t.Insert(kv);
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
- 命名空间
MyMap
中,定义了一个内部结构体MapKeyOfT
,它是一个仿函数,用于从键值对中提取键。在这个仿函数中,重载了函数调用操作符operator()
,接受一个键值对kv
作为参数,并返回该键值对中的键first
。 - 接下来是
map
类的定义。它使用了两个模板参数K
和V
,分别表示键和值的类型。在map
类中,使用了RBTree
类来实现红黑树的功能。RBTree
的模板参数为K
、pair<const K, V>
和MapKeyOfT
,表示键的类型、键值对的类型和键提取仿函数的类型。 map
类中定义了一个迭代器类型iterator
,它是RBTree<K, pair<const K, V>, MapKeyOfT>::iterator
的别名。这个迭代器类型用于遍历映射中的键值对。map
类提供了begin
和end
方法,用于返回映射的起始迭代器和结束迭代器。begin
方法返回的是红黑树的起始迭代器,而end
方法返回的是红黑树的结束迭代器。- 此外,
map
类还重载了下标操作符[]
,使得可以通过键访问对应的值。它接受一个键key
作为参数,并返回对应的值的引用。如果键不存在于映射中,则会自动插入一个新的键值对,并返回对应值的引用。 map
类还提供了insert
方法,用于向映射中插入键值对。它接受一个键值对kv
作为参数,并返回一个pair
对象,其中包含一个迭代器和一个布尔值。迭代器指向插入的键值对,布尔值表示插入是否成功。- 最后,
map
类中有一个私有成员变量_t
,它是一个RBTree<K, pair<const K, V>, MapKeyOfT>
类型的对象,用于存储映射的键值对。