当前位置: 首页 > news >正文

C++--红黑树封装实现set和map

set和map的模拟实现

  • 红黑树封装实现 map 和 set
    • 1. 源码及框架分析
      • 1.1 头文件分析
      • 1.2 容器成员变量即接口分析
      • 1.3 有关基类红黑树的分析
    • 2. 模拟实现map和set
      • 2.1 实现复用红黑树框架
      • 2.2 模拟实现insert
      • 2.3 模拟实现底层红黑树
        • 2.3.1 红黑树的迭代器
          • 2.3.1.1 迭代器的++与--
          • 2.3.1.2 end() 迭代器的位置
        • 2.3.2 完整代码(RBtree.h)
      • 2.4 模拟实现map中的[]
      • 2.5 模拟实现set
        • 2.5.1 完整代码(Myset.h)
      • 2.6 模拟实现map
        • 2.6.1 完整代码(Mymap.h)

红黑树封装实现 map 和 set

1. 源码及框架分析

之前在学习 set 和 map 基本使用时就介绍了 set 和 map 底层都是红黑树,但这里有一个问题:set 是K模型的容器,而 map 是KV模型的容器,那它们底层为什么能同样都使用红黑树呢?可以通过阅读源码来解答这个问题。(本文中使用的源码为 SGI 的 stl30 版本)

1.1 头文件分析

//map
#include <stl_tree.h>
#include <stl_map.h>
#include <stl_multimap.h>//set
#include <stl_tree.h>
#include <stl_set.h>
#include <stl_multiset.h>

可以看到,stl map 和 set 头文件中分别包含了三个头文件,其中 stl_tree.h 是红黑树,stl_map.h 和 stl_set.h 是 map 和 set,而 stl_multimap.h 和 stl_multiset.h 则是 multimap 和 multiset。

这就是为什么使用 multiset 和 multimap 时只需要包 set 和 map 头文件的原因。

1.2 容器成员变量即接口分析

//stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set 
{
public:// typedefs:typedef Key key_type;typedef Key value_type
private:typedef rb_tree<key_type, value_type, identity<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing set
};//stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc
class map 
{
public:// typedefs:typedef Key key_type;typedef T mapped_type;typedef pair<const Key, T> value_type;
private:typedef rb_tree<key_type, value_type,select1st<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing map
};

可以看到,set 和 map 的插入删除查找等各种功能都是封装复用的红黑树的接口,对于 set 来说,key_type 也是平时认为的 K,但是发现 set 中居然还有一个变量 value_type,并且 value_type 的类型就是 key_type 的类型,但是 set 不是K模型的容器吗?它里面为什么要设计一个 value_type 变量呢?要解答这个问题,还得阅读红黑树的源码。

而对于 map来说,key_type 就是平常所理解的 K,但是 value_type 却是 pair 类型,它的两个参数分别是模板参数 _Key 和 _Tp,也就是正常认为的传递给 map 的 K 和 V。

1.3 有关基类红黑树的分析

// stl_tree.h
//红黑树基类
struct __rb_tree_node_base
{typedef __rb_tree_color_type color_type;typedef __rb_tree_node_base* base_ptr;color_type color; base_ptr parent;base_ptr left;base_ptr right;
};//红黑树的节点结构
template<typename Value>
struct _rb_tree_node : public _rb_tree_node_base
{typedef rb_tree_node<_Val>* _link_type;Value value_field;
};//红黑树
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc= alloc
class rb_tree 
{
protected:typedef void* void_pointer;typedef __rb_tree_node_base* base_ptr;typedef __rb_tree_node<Value> rb_tree_node;typedef rb_tree_node* link_type;typedef Key key_type;typedef Value value_type;
public:// insert⽤的是第⼆个模板参数左形参 pair<iterator,bool> insert_unique(const value_type& x);// erase和find⽤第⼀个模板参数做形参 size_type erase(const key_type& x);iterator find(const key_type& x);protected:size_type node_count; // keeps track of size of treelink_type header;
};

可以看到,红黑树中定义了一个基类 _rb_tree_node_base,基类中定义了节点的颜色和三叉链结构,然后让 _rb_tree_node 去继承基类,并在类中增加成员变量 value_field,value_field 的类型是 Value,而这个 Value 恰好是红黑树的第二个模板参数,也就是 map 和 set 传递过来的 value_type,如下:

在这里插入图片描述

通过上图对框架的分析,可以看到源码中 rb_tree 用了一个巧妙的泛型思想实现,rb_tree 是实现 key 的搜索场景,还是 key/value 的搜索场景不是直接写死的,而是由第二个模板参数 Value 决定 _rb_tree_node 中存储的数据类型。

  • set 实例化 rb_tree 时第二个模板参数给的是 Key
  • map 实例化 rb_tree 时第二个模板参数给的是 pair<const Key, T>

这样一颗红黑树既可以实现 key 搜索场景的 set,也可以实现 key/value 搜索场景的 map

补充:

要注意,源码里面模板参数用 T 代表 value,而内部写的 value_type 并不日常 key/value 场景中说的 value,源码中的 value_type 反而是红黑树节点中存储的真实数据的类型。

这里源码的命名风格比较乱,set 模版参数用的 Key 命名,map 用的是 Key 和 T 命名,而 rb_tree 用的又是 Key 和 Value。

问题:

rb_tree 第二个模板参数 Value 已经控制了红黑树节点中存储的数据类型,为什么还要传第一个模板参数 Key 呢?尤其是 set,两个模板参数是一样的。

要注意的是对于 map 和 set,实现 find/erase 时的函数参数都是Key,所以第一个模板参数是传给 find/erase 等函数做形参的类型。对于 set 而言两个参数是一样的,但是对于 map 而言就完全不一样了,map insert的是pair对象,但是 find 和 erase 的是 Key 对象。

2. 模拟实现map和set

2.1 实现复用红黑树框架

参考源码框架,map 和 set 复用之前实现的红黑树,并且对比源码进行一些调整,Key 的参数用 K ,Value 的参数就用 V ,红黑树中的数据类型就用 T。

// RBTree.h
enum Colour{RED,BLACK
};template<class T>
struct RBTreeNode
{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){}
};template<class K, class T, class KeyOfT>
class RBTree
{
private:typedef RBTreeNode<T> Node;Node* _root = nullptr;public://...
}// Myset.h
namespace tcq
{template<class K>class set{//...};
}// Mymap.h
namespace tcq
{template<class K, class V>class map{//...};
}

补充:

自行模拟实现的 map 和 set 使用命名空间分隔开,避免和标准库中的 map 和 set 形成冲突。

2.2 模拟实现insert

因为 RBTree 实现了泛型,不知道 T 参数是 K,还是 pair<K, V>,那么 insert 内部进行插入逻辑比较时,就没办法进行比较,因为 pair 的默认支持的是 key 和 value 一起参与比较,而需要的任何时候只比较 key,所以在 map 和 set 层分别实现一个 MapKeyOfTSetKeyOfT 的仿函数传给 RBTree 的第三个参数 KeyOfT,然后 RBTree 中通过 KeyOfT 仿函数取出 T 类型对象中的 key,再进行比较,即可实现 insert 的功能,具体细节参考如下代码实现。

// 源码中pair⽀持的<重载实现>
template <class T1, class T2>
bool operator< (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs)
{ return lhs.first<rhs.first || (!(rhs.first<lhs.first) && 
lhs.second<rhs.second); 
}
// Myset.h
namespace tcq
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:bool insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}
// Mymap.h
namespace bit
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:bool insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<K, V>, MapKeyOfT> _t;};
}
//RBTree.h
//map: RBTree<K, pair<K,V>, MapKeyOfT> _t;
//set: RBTree<K, K, SetKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{
private:typedef RBTreeNode<T> Node;Node* _root = nullptr;
public:bool Insert(const T& data){//判断根节点是否为空if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;//根节点的颜色一定为黑return 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 false;//不允许重复节点}}//走到空开始插入,注意这里还需要修改父节点的指向//新增节点的颜色为默认被初始化为红色,所以这里不需要再显式赋值cur = new Node(data);Node* newnode = cur;// 新增结点,颜⾊给红⾊ cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//修改父节点指向//调整结点...return true;}
}

2.3 模拟实现底层红黑树

2.3.1 红黑树的迭代器

iterator 实现的大框架跟 list 的 iterator 思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为,比如 operator*() 返回节点中的数据,operator->() 返回节点的地址,operator!=() 比较两个迭代器是否相等,其中,为了满足 const 迭代器返回 const T& 和 const T*,需要增加两个模板参数 Ref 和 Ptr。

//红黑树的迭代器
//__RBTreeIterator<T, T&, T*>  			   //普通迭代器
//__RBTreeIterator<T, const T&, const T*>  //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){}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;}
};

以上要求在模拟实现list 的时候都已经详细介绍过了所以这里不再赘述。红黑树的迭代器和 list 迭代器不同的地方在于红黑树 end() 迭代器的位置以及迭代器如何进行 ++ 与 –

2.3.1.1 迭代器的++与–

迭代器 – 的实现跟 ++ 的思路完全类似,逻辑正好反过来即可,因为它访问顺序是右子树->根结点->左子树,所以下面重点对 ++ 进行详细介绍。迭代器++的核心逻辑就是不看全局,只看局部,只考虑当前中序局部要访问的下一个结点。

  • **情况一:**迭代器++时,如果 it 指向的结点的右子树不为空,代表当前结点已经访问完了,要访问下一个结点是右子树的中序第一个;一棵树中序第一个是最左结点,所以直接找右子树的最左结点即可。
  • **情况2:**迭代器++时,如果 it 指向的结点的右子树为空,代表当前结点已经访问完了且当前结点所在的子树也访问完了,要访问的下一个结点在当前结点的祖先里面,所以要沿着当前结点到根的祖先路径向上找。

示例分析:

img

如果当前结点是父亲的左,根据中序左子树->根结点->右子树,那么下一个访问的结点就是当前结点的父亲;如上图:it指向15,15右为空,15是17的左,所以下一个访问的结点就是17。

如果当前结点是父亲的右,根据中序左子树->根结点->右子树,当前结点所在的子树访问完了,当前结点所在父亲的子树也访问完了,那么下一个访问的需要继续往根的祖先中去找,直到找到孩子是父亲左的那个祖先(目标节点的左孩子是当前节点的父亲)就是中序要访问的下一个结点。如图:it指向11,11右为空,11是8的右,11所在子树访问完了,8所在子树也访问完了,继续往上找,8是13的左,那么下一个访问的结点就是13。

代码实现:

// RBTree.h
//红黑树的迭代器
//__RBTreeIterator<T, T&, T*>  				//普通迭代器
//__RBTreeIterator<T, const T&, const T*>  	//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){}//++迭代器Self& operator++() {//如果右子树不为空,则++迭代器为右子树的最左节点if (_node->_right) {Node* cur = _node->_right;while (cur->_left)cur = cur->_left;_node = cur;}//如果右子树为空,则++迭代器为 cur为父节点的左孩子的那一个祖先节点else {Node* cur = _node;Node* parent = cur->_parent;while (parent && cur != parent->_left) {cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//迭代器++Self& operator++(int) {Self& tmp = *this;operator++();return tmp;}//--迭代器Self& operator--() {//如果左子树存在,则--迭代器为左子树的最右节点if (_node->_left) {Node* cur = _node->_left;while (cur->_right)cur = cur->_right;_node = cur;}//如果左子树为空,则--迭代器为 cur为父节点的右孩子的那一个祖先节点else {Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right != cur) {cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//迭代器--Self& operator--(int) {Self& tmp = *this;operator--();return tmp;}
};
2.3.1.2 end() 迭代器的位置

STL 明确规定,begin() 与 end() 代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此,begin() 可以放在红黑树中最小节点 (即最左侧节点) 的位置,end() 可以放在最大节点 (最右侧节点) 的下一个位置。为了让 end() 能够指向最右节点的下一个节点, stl 源码中增加了一个哨兵位的头结点,该节点的 left 指向这棵树的最左节点,也就是 begin(),right 指向这棵树的最右节点,parent 指向 nullptr,然后让根的父节点指向它,最右节点的 right 也指向它,所以在 stl 源码的实现中这个哨兵位头结点就是 end()。

在这里插入图片描述

在这里插入图片描述

如上图:当 it 指向 50 时,执行 ++it,50 是 40 的右子节点,40 是 30 的右子节点,30 是 18 的右子节点,18 到根没有父亲,也没有找到“孩子是父亲左子节点”的那个祖先,此时父亲为空,就将 it 中的节点指针置为 nullptr,用 nullptr 去充当 end()

// RBTree.h
template<class K, class T, class KeyOfT>
class RBTree 
{typedef RBTreeNode<T> Node;
public://迭代器typedef __RBTreeIterator<T, T&, T*> iterator;typedef __RBTreeIterator<T, const T&, const T*> const_iterator;//begin是树的最左节点iterator begin() {if (_root == nullptr)return iterator(nullptr);Node* left = _root;while (left->_left) {left = left->_left;}return iterator(left);}const_iterator begin() const {if (_root == nullptr)return const_iterator(nullptr);Node* left = _root;while (left->_left) {left = left->_left;}return const_iterator(left);}//end定义为空iterator end() {return iterator(nullptr);}const_iterator end() const {return const_iterator(nullptr);}
};

以上所介绍的都是 set 和 map 底层红黑树的迭代器的设计,对于 set 和 map 的迭代器还有一下其他的要求。具体介绍在下面模拟实现 set 和 map 中介绍。

2.3.2 完整代码(RBtree.h)
// RBtree.h
enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{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){}
};template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;Node* _root;RBTreeIterator(Node* node, Node* root):_node(node), _root(root){}Self& operator++(){if (_node->_right){// 右不为空,右⼦树最左结点就是中序第⼀个 Node* leftMost = _node->_right;while (leftMost->_left){leftMost = leftMost->_left;}_node = leftMost;}else{// 孩⼦是⽗亲左的那个祖先 Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = parent;parent = cur->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node == nullptr) // end(){// --end(),特殊处理,⾛到中序最后⼀个结点,整棵树的最右结点 Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else if (_node->_left){// 左⼦树不为空,中序左⼦树最后⼀个 Node* rightMost = _node->_left;while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else{// 孩⼦是⽗亲右的那个祖先 Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}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;}
};template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef RBTreeIterator<T, T&, T*> Iterator;typedef RBTreeIterator<T, const T&, const T*> ConstIterator;Iterator Begin(){Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return Iterator(leftMost, _root);}Iterator End(){return Iterator(nullptr, _root);}ConstIterator Begin() const{Node* leftMost = _root;while (leftMost && leftMost->_left){leftMost = leftMost->_left;}return ConstIterator(leftMost, _root);}ConstIterator End() const{return ConstIterator(nullptr, _root);}RBTree() = default;~RBTree(){Destroy(_root);_root = nullptr;}pair<Iterator, bool> Insert(const T & data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(Iterator(_root, _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(Iterator(cur, _root), false);}}cur = new Node(data);Node* newnode = cur;// 新增结点。颜⾊红⾊给红⾊ cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;// g// p uif (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// u存在且为红 -》变⾊再继续往上处理 parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{// u存在且为⿊或不存在 -》旋转+变⾊ if (cur == parent->_left){// g// p u//c//单旋 RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// p u//  c//双旋 RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{// g// u pNode* uncle = grandfather->_left;// 叔叔存在且为红,-》变⾊即可 if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理 cur = grandfather;parent = cur->_parent;}else // 叔叔不存在,或者存在且为⿊ {// 情况⼆:叔叔不存在或者存在且为⿊ // 旋转+变⾊ // g// u p//	  cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//  g// u p//  cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode, _root), true);}Iterator 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 Iterator(cur, _root);}}return End();}private:void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_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;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent; subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}private:Node* _root = nullptr;
};

2.4 模拟实现map中的[]

map 要支持 [] 主要需要修改 insert 返回值支持,修改 RBTree 中的 insert 返回值为 pair<Iterator, bool> Insert(const T& data)

// RBTree.h
//修改后的插入
pair<Iterator, bool> Insert(const T& data)
{// 判断根节点是否为空if (_root == nullptr) {_root = new Node(kv);_root->_col = BLACK;  // 根节点的颜色一定为黑return make_pair(Iterator(_root, _root), true);}// 寻找插入位置KeyOfT kotNode* parent = nullptr;Node* cur = _root;while (cur) {if (kot(cur->_data) > kot(data)) {parent = cur;cur = cur->_left;}else if (kot(cur->_data) < kot(data)) {parent = cur;cur = cur->_right;}else {return return make_pair(Iterator(cur, _root), false);  // 不允许重复节点}}// 走到空开始插入,注意这里还需要修改父节点的指向// 新增节点的颜色为默认被初始化为红色,所以这里不需要再显式赋值cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) > kot(data))parent->_left = cur;elseparent->_right = newNode;cur->_parent = parent;  // 修改父节点指向// 如果父节点颜色为红色,则需要进行调整while(parent && parent->_col == RED){Node* grandfather = parent->_parent;// 红黑树的调整//	g// p u// 父节点在组父节点左边if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 第一种调整情况:叔叔存在且为红if (uncle && uncle->_col == RED){// u存在且为红 ->变⾊再继续往上处理 parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}// 第二种调整情况:叔叔不存在或叔叔存在且为黑else{// u存在且为黑或不存在 -> 旋转+变⾊ if (cur == parent->_left){//   g//  p u// c// 单旋 RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//  g// p u//  c// 双旋 RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}//	g// u p// 父节点在组父节点右边else{Node* uncle = grandfather->_left;// 第一种调整情况:叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理 cur = grandfather;parent = cur->_parent;}// 第二种调整情况:叔叔不存在或叔叔存在且为黑else{// u存在且为黑或不存在 -> 旋转+变⾊ if (cur == parent->_right){//  g// u p//    c// 单旋RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//  g// u p//  c// 双旋RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode, _root), true);
}// Mymap.h
V& operator[](const K& key)
{pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;
}

2.5 模拟实现set

set 模拟实现的前面部分很简单,在类中定义一个 RBTree 类型的成员变量,然后插入、查找等函数都直接调用红黑树的对应函数即可。set 模拟实现的重难点在于 set 迭代器的实现,set的iterator也不支持修改,把set的第二个模板参数改成const K即可, 如下:

//使用红黑树的const迭代器来封装set的普通迭代器
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
2.5.1 完整代码(Myset.h)
// Myset.h
#include"RBTree.h"
namespace tcq
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}iterator find(const K& key){return _t.Find(key);}private:RBTree<K, const K, SetKeyOfT> _t;};
}

2.6 模拟实现map

map 和 set 一样,模拟实现的前面部分很简单如插入、查找都直接复用红黑树的相应函数实现;不同的是,map 是 KV模型 的容器, KV模型 的 key 虽然也不允许修改,因为这可能会破坏搜索树的结构,但是 value 是运行修改的,所以 map 的普通迭代器封装红黑树的普通迭代器即可,而不用将其封装为红黑树的 const 迭代器。

同时,也不用担心 map 的 key 被修改的问题,因为 map 在定义红黑树的成员变量时传递给红黑树的 T 模板参数为 pair<const K, V>,它保证了 map 的 key 值不能被修改

typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;//const K保证了map的key不会被修改
RBTree<K, std::pair<const K, V>, MapKeyOfT> _t;

map 模拟实现的难点在于 operator[](const K& key) 函数的实现前文已经对此内容进行了介绍这里就不过多赘述。

2.6.1 完整代码(Mymap.h)
// Mymap.h
#include"RBTree.h"
namespace bit
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<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>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}iterator find(const K& key){return _t.Find(key);}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}
http://www.xdnf.cn/news/1118305.html

相关文章:

  • 极矢量与轴矢量
  • Linux系统移植19:根文件系统的构建
  • leetGPU解题笔记(2)
  • C# 接口(接口可以继承接口)
  • 华为OD 处理器
  • 改进后的 OpenCV 5.x + GStreamer + Python 3.12 编译流程(适用于 Orange Pi / ARM64)
  • vue的优缺点
  • Vue 3 TypeScript 接口(Interface)使用
  • 【基于开源大模型(如deepseek)开发应用及其发展趋势的一点思考】
  • 西藏氆氇新生:牦牛绒混搭液态金属的先锋尝试
  • web:js的三种引用方式
  • MYSQL笔记1
  • 大模型之Langchain篇(二)——RAG
  • SQL的初步学习(二)(以MySQL为例)
  • 《区间dp》
  • Excalidraw:一款颠覆传统思维的免费开源绘图工具
  • DHS及HTTPS工作过程
  • JSON/AJAX/XHR/FetchAPI知识点学习整理
  • 代码随想录算法训练营第三十二天|动态规划理论基础、LeetCode 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
  • std::sort的核心设计思想
  • 代码随想录算法训练营第十七天
  • MongoDB数据基本介绍
  • 从 Intel MacBook 迁移到 ARM MacBook 的完整指南
  • Windows怎样同步时间服务器?
  • 【网络实验】-BGP选路原则-11条
  • 攻防世界——Web题 very_easy_sql
  • 嵌入式 Linux开发环境构建之安装 SSH 软件
  • Spring AI 项目实战(十六):Spring Boot + AI + 通义万相图像生成工具全栈项目实战(附完整源码)
  • mapstruct与lombok冲突原因及解决方案
  • 2025年渗透测试面试题总结-2025年HW(护网面试) 44(题目+回答)