STL——红黑树的封装及map/set的模拟实现
前言
前面的文章当中,我们介绍了红黑树的模拟实现,我们了解到红黑树是一颗不完全平衡二叉搜索树,我们之所以要学习红黑树,不仅仅是学习它实现的思维逻辑,更多的是通过红黑树去学习map和set的底层,前面我们介绍map/set的使用中我们提到,他们的底层是用红黑树去封装实现的,那么具体是怎么完成的呢?我们将在这一篇文章当中介绍。
参考文章:
STL——map和set的介绍
数据结构(六)——红黑树及模拟实现
map/set底层代码剖析
打开STL源码,我们找到红黑树的代码,以及map/set的源码,如下所示:
红黑树的源码
enum _Rb_tree_color { _S_red = false, _S_black = true };struct _Rb_tree_node_base
{typedef _Rb_tree_node_base* _Base_ptr;typedef const _Rb_tree_node_base* _Const_Base_ptr;_Rb_tree_color _M_color;_Base_ptr _M_parent;_Base_ptr _M_left;_Base_ptr _M_right;
}template<typename _Val>struct _Rb_tree_node : public _Rb_tree_node_base{typedef _Rb_tree_node<_Val>* _Link_type;_Val _M_value_field;}template<typename _Key, typename _Val, typename _KeyOfValue,typename _Compare, typename _Alloc = allocator<_Val> >class _Rb_tree{typedef typename __gnu_cxx::__alloc_traits<_Alloc>::templaterebind<_Rb_tree_node<_Val> >::other _Node_allocator;typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;protected:typedef _Rb_tree_node_base* _Base_ptr;typedef const _Rb_tree_node_base* _Const_Base_ptr;public:typedef _Key key_type;typedef _Val value_type;typedef value_type* pointer;typedef const value_type* const_pointer;typedef value_type& reference;typedef const value_type& const_reference;typedef _Rb_tree_node<_Val>* _Link_type;typedef const _Rb_tree_node<_Val>* _Const_Link_type;typedef size_t size_type;typedef ptrdiff_t difference_type;typedef _Alloc allocator_type;
protected:_Link_type;
}
如上所示就是STL当中红黑树源码的一部分截取,我们看到库中的红黑树使用了继承的实现方法,红黑树节点当中的只保留了一个模板参数_val,而红黑树的结构当中一共有五个模板参数,这五个模板参数分别是key,val,keyofvalue,仿函数以及空间配置器,对比我们之前模拟实现的红黑树,我们知道,如果我们要实现key模型则只需要传一个模板参数Key,如果我们要实现K而言,value模型我们则需要传两个模板参数,模板参数与存储的数据个数是一一对应的关系,但是对于库当中而言,它的节点里面只有一个模板参数,而它的结构当中有多个不同的模板参数,这是为什么呢?
我们从底层实现角度去分析,我们知道,红黑树是作为map/set的底层去封装实现map/set的,这两个结构中参数个数是不同的,所以我们在实例化之前是不知道红黑树的节点当中存放什么值的,如果我们传两个模板参数,那么对于set而言有一个模板参数是没有定义的;如果我们传一个模板参数,对于map而言,无法保存第二个参数,所以源码当中对于红黑树节点当中只保存一个模板参数,这个模板参数具体是什么由用户传的参数所决定,为了做到用户传不同的参数,实例化出不同的结构这种效果,库中对于红黑树的结构的实现,分别传了三个不同的模板参数。
那么具体是如何实现的呢?我们还需要结合map/set的源码去观察。
map/set的源码
map的源码:
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >class map{public:typedef _Key key_type;typedef _Tp mapped_type;typedef std::pair<const _Key, _Tp> value_type;typedef _Compare key_compare;typedef _Alloc allocator_type;public:class value_compare: public std::binary_function<value_type, value_type, bool>{friend class map<_Key, _Tp, _Compare, _Alloc>;protected:_Compare comp;public:bool operator()(const value_type& __x, const value_type& __y) const{ return comp(__x.first, __y.first); }};private:/// This turns a red-black tree into a [multi]map. typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
rebind<value_type>::other _Pair_alloc_type;typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,key_compare, _Pair_alloc_type> _Rep_type;/// The actual tree structure._Rep_type _M_t;typedef __gnu_cxx::__alloc_traits<_Pair_alloc_type> _Alloc_traits;public:// many of these are specified differently in ISO, but the following are// "functionally equivalent"typedef typename _Alloc_traits::pointer pointer;typedef typename _Alloc_traits::const_pointer const_pointer;typedef typename _Alloc_traits::reference reference;typedef typename _Alloc_traits::const_reference const_reference;typedef typename _Rep_type::iterator iterator;typedef typename _Rep_type::const_iterator const_iterator;typedef typename _Rep_type::size_type size_type;typedef typename _Rep_type::difference_type difference_type;typedef typename _Rep_type::reverse_iterator reverse_iterator;typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;
}
set的源码:
template<typename _Key, typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<_Key> >class set{// concept requirementstypedef typename _Alloc::value_type _Alloc_value_type;__glibcxx_class_requires(_Key, _SGIAssignableConcept)__glibcxx_class_requires4(_Compare, bool, _Key, _Key,_BinaryFunctionConcept)__glibcxx_class_requires2(_Key, _Alloc_value_type, _SameTypeConcept)public:typedef _Key key_type;typedef _Key value_type;typedef _Compare key_compare;typedef _Compare value_compare;typedef _Alloc allocator_type;public://@{/// Iterator-related typedefs.typedef typename _Alloc_traits::pointer pointer;typedef typename _Alloc_traits::const_pointer const_pointer;typedef typename _Alloc_traits::reference reference;typedef typename _Alloc_traits::const_reference const_reference;// _GLIBCXX_RESOLVE_LIB_DEFECTS// DR 103. set::iterator is required to be modifiable,// but this allows modification of keys.typedef typename _Rep_type::const_iterator iterator;typedef typename _Rep_type::const_iterator const_iterator;typedef typename _Rep_type::const_reverse_iterator reverse_iterator;typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;typedef typename _Rep_type::size_type size_type;typedef typename _Rep_type::difference_type difference_type;
}
我们还是看到模板参数上面,我们发现map/set的第一个模板参数都是_Key,第二个模板参数都是T,那么我们可以猜测,第二个模板参数才是我们要传的真实值,我们将第二个模板参数传给红黑树,红黑树识别参数类型后将该参数保存至红黑树的节点中进行储存,这样我们就通过传不同的模板参数,将同一颗红黑树实例化成map或者set,这是一种巧妙的泛型思想。
综上所述,我们可以看到,源码当中红黑树是实现key的场景还是实现key_value的场景不是写死的,而是通过红黑树的第二个模板参数value决定的。如果我们对第二个模板参数传K,则实现set,如果我们对第二个模板参数传pair<K,V>,则实现map。
那么我们此时会有一种疑惑:既然都是传第二个模板参数,那么为什么红黑树的结构当中还需要保存第一个模板参数呢?实际上第一个模板参数是为了查找/修改做准备的。具体实现细节我们将在下面的内容当中展现。
红黑树的封装
前面我们模拟实现了红黑树的查找,插入,遍历等功能,那么为了让它作为底层去封装实现map/set我们需要对前面的代码进行修改,其实现过程如下所示:
红黑树的结构
enum Colour
{BLACK,RED
};
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
{typedef RBTreeNode<T> Node;
public:
private:Node* _root = nullptr;
};
修改后的红黑树的节点当中只保留一个模板参数,红黑树的结构当中我们传了两个模板参数据,一个是K,一个是T,后续通过我们传不同的模板参数T,去封装实现set或者map。
我们发现,我们还传了第三个模板参数KeyOfT,这个参数的作用我们将在后面解释。
红黑树迭代器的实现
红黑树的迭代器的实现思路与list的迭代器的实现有异曲同工之妙,我们的实现方法都是将迭代器封装成一个类,在这个类当中用函数重载去实现迭代器的各种功能。
我们都知道,迭代器是为了访问当前节点,所以我们应当在迭代器类中保留当前节点的指针,这样我们就可以在迭代器内部访问到当前节点,因此迭代器的结构如下所示:
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){}
};
仔细观察我们看到,我们除了保存当前节点的指针,我们还保留了根节点,这是为什么呢?实际上这个根节点是为了解决前置--的问题的,我们将在下面逐一解答。
至于后面两个模板参数Ref和Ptr这也是为了复用做准备,后续我们将实现const迭代器,如果我们不传Ref和Ptr这两个模板参数,后续我们还要实现一个const迭代器的类,那么如果我们不实现const迭代器的类,又要得到const的迭代器,我们就要为它增加模板参数,通过实例化不同的模板参数,让编译器帮我们实现。
前置++/后置++
红黑树是一颗树形结构,它的访问顺序是:左子树->根->右子树,如果我们想要访问到下一个位置,我们就需要遍历这颗树,它分为下面两种情况:
情况一:如果当前节点的右子树不为空,如果我们要访问下一个节点,那么下一个节点就是右子树的最左侧节点
情况二:如果当前节点的右子树为空,代表当前节点的子树已经访问完毕,我们接下来访问的节点就在当前节点的祖先里面,所以我们要继续向上寻找。
其代码如下所示:
Self& operator++()
{if (_node->_right){Node* right_less = _node->_right;while (right_less->_left){right_less = right_less->_left;}_node = right_less;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}Self operator++(int)
{Self tmp(*this);++*this;return tmp;
}
前置--/后置--
--的逻辑相对于++而言有所区别,首先我们要知道--的访问顺序,那么就是和++相反的方向,即:右子树->根节点->左子树,如果我们要访问到下一个节点,还是需要遍历这棵树,同样分为两种不同的情况:
情况一:如果当前节点的左子树不为空,那么我们下一个要访问的节点就是当前节点左子树的最大节点,即:当前节点左子树的最右侧节点。
情况二:如果当前节点的左子树为空,代表着当前子树已经遍历完了,下一个节点在它的祖先里面,所以我们要继续向上找。
到此我们可以看到,--的逻辑实际上与++的逻辑正好相反,那么--就这么结束了吗?我们此时思考一个问题,end()的位置在哪里呢?它在最后一个节点的下一个位置,对于树形结构而言,最后一个节点的下一个位置为空节点,如果迭代器走到了最后end的位置,如果此时--我们将找不到最后一个节点,对于这个问题,库里面是怎么解决的呢?STL库当中增加了一个header节点,这个节点去充当哨兵位的头节点,哨兵位的头节点和根互为父亲,它的左子树指向最左侧节点(begin的位置),它的右子树指向最右侧节点(--end的位置),这样就巧妙的解决了这个问题。
那么我们应该怎么解决这个问题呢?我们的解决方法是将end的位置置为空,如果迭代器位于end位置要--时,我们直接遍历这棵树,然后找到它的最右侧节点,这样就解决了这个问题。
但是此时我们又发现了一个新问题,我们该如何得到根节点的指针呢?我的解决办法是:直接在迭代器当中保存根节点的指针,这样就解释了为什么我们要在迭代器里面保存根节点。
其代码如下所示:
Self& operator--()
{if (_node == nullptr){Node* right_most = _root;while (right_most->_right){right_most = right_most->_right;}_node = right_most;}else if (_node->_left){Node* left_most = _node->_left;while (left_most->_right){left_most = left_most->_right;}_node = left_most;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}
Self operator--(int)
{Self tmp(*this);--*this;return tmp;
}
operator !=/==
bool operator!=(const Self& s)const
{return _node != s._node;
}bool operator==(const Self& s)const
{return _node == s._node;
}
operator */->
Ref operator*()
{return _node->_data;
}
Ptr operator->()
{return &_node->_data;
}
观察上面的代码,我们知道Ref是引用,Ptr是指针,如果我们要实现const的iterator只需要修改这两个函数的返回值就可以实现,因此我们可以在定义的时候多传两个模板参数Ref和Ptr,通过传不同的参数,实例化出不同的代码。
完整的迭代器代码如下
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* right_less = _node->_right;while (right_less->_left){right_less = right_less->_left;}_node = right_less;}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){Node* right_most = _root;while (right_most->_right){right_most = right_most->_right;}_node = right_most;}else if (_node->_left){Node* left_most = _node->_left;while (left_most->_right){left_most = left_most->_right;}_node = left_most;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}Self operator++(int){Self tmp(*this);++*this;return tmp;}Self operator--(int){Self tmp(*this);--*this;return tmp;}bool operator!=(const Self& s)const{return _node != s._node;}bool operator==(const Self& s)const{return _node == s._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}
};
begin与end
有了红黑树的迭代器的类,我们可以通过迭代器去访问到红黑树的begin与end,对于begin而言,它的第一个位置是最左节点,所以我们需要去找到红黑树的最左节点,我们还是用遍历的方式,如当前节点不为空,并且当前节点的左节点不为空,则则向左遍历,其代码如下所示:
Iterator Begin()
{Node* cur = _root;while (cur&&cur->_left){cur = cur->_left;}return Iterator(cur, _root);
}
对于end而言我们直接将其置为空,其代码如下所示:
Iterator End()
{return Iterator(nullptr, _root);
}
红黑树的析构
对于红黑树的销毁,我们采用左子树->右子树->根节点的顺序,使用递归的方式,其代码如下所示:
void InOrder()
{_InOrder(_root);cout << endl;
}void Destroy(Node*root)
{if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;
}
红黑树插入代码的修改
由于我们需要使用同一个红黑树通过传不同的模板参数去实现map和set,所以我们需要对插入代码进行修改。
插入代码的修改逻辑我们以实现map为例。我们知道map有两个不同的模板参数,为了将这两个模板参数都传给红黑树的第二个模板参数我们需要用pair传参,但是对于红黑树的插入而言,我们需要比较第一个模板参数的大小,由于我们对红黑树的第二个模板参数传的是pair类型,如果我们想要访问到pair当中的第一个参数,在红黑树当中是无法做到的,这是因为map和红黑树属于不同的类,我们无法在红黑树当中对map进行操作,即:我们无法对模板参数里面的数据类型进行比较。
那么我们该如何才能在红黑树当中完成pair第一个模板参数的比较呢?我们需要一个新的类模板参数,然后在这个类模板参数当中实现数据访问的接口,这样我们就可以在红黑树当中完成pair模板参数当中第一个数据大小的比较,而这个类模板参数的名称是:KeyOfT,由此就解释了红黑树当中第三个模板参数的由来。
那么这个类模板参数是如何实现对pair当中第一个模板参数的比较呢?答案是在map当中实现一个名称为KeyOfT的类,在这个类当中实现一个仿函数,通过这个仿函数找到pair的第一个数据。其代码如下所示:
struct MapKeyOfT
{const K& operator()(const pair<K, V>&kv){return kv.first;}
};
由此,我们对红黑树的插入代码修改如下所示:
public:typedef RBTreeIterator<T, T&, T*> Iterator;
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* cur = _root;Node* parent = nullptr;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{cout << "插入值已经存在,请重新插入" << endl;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;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode,_root),true);
}
分析上述代码,我们看到,我们修改了insert的返回值,我们用pair<iterator,bool>去接收insert的返回值,这也是为了实现泛型而做出的改变,因为我们不清楚插入的数据是Key类型的还是Key_Value类型的,我们只有在传参的时候才能知道,所以就用了pair作为返回值,让编译器帮我们生成。
红黑树的查找
Iterator Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) > key){cur = cur->_left;}else if (kot(cur->_data) < key){cur = cur->_right;}else{return Iterator(cur, _root);}}return End();}
仔细观察我们看到,红黑树的查找我们传的不再是第二个模板参数,我们传的是第一个模板参数K,这是一个很巧妙的泛型思想,如果我们要实现set,那么第一个模板参数就是我们要查找的值;如果我们要实现map,那么第一个模板参数就是pair的第一个参数。
红黑树的封装代码如下所示:
enum Colour
{BLACK,RED
};
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* right_less = _node->_right;while (right_less->_left){right_less = right_less->_left;}_node = right_less;}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){Node* right_most = _root;while (right_most->_right){right_most = right_most->_right;}_node = right_most;}else if (_node->_left){Node* left_most = _node->_left;while (left_most->_right){left_most = left_most->_right;}_node = left_most;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}Self operator++(int){Self tmp(*this);++*this;return tmp;}Self operator--(int){Self tmp(*this);--*this;return tmp;}bool operator!=(const Self& s)const{return _node != s._node;}bool operator==(const Self& s)const{return _node == s._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}
};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;Iterator Begin(){Node* cur = _root;while (cur&&cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Iterator End(){return Iterator(nullptr, _root);}const_Iterator Begin()const{Node* cur = _root;while (cur&&cur->_left){cur = cur->_left;}return const_Iterator(cur, _root);}const_Iterator End()const{return const_Iterator(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* cur = _root;Node* parent = nullptr;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{cout << "插入值已经存在,请重新插入" << endl;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;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode,_root),true);}void InOrder(){_InOrder(_root);cout << endl;}Iterator Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) > key){cur = cur->_left;}else if (kot(cur->_data) < key){cur = cur->_right;}else{return Iterator(cur, _root);}}return End();}
private:void _InOrder(Node* root){if (root == nullptr){return;}KeyOfT kot;_InOrder(root->_left);cout <<kot(root->_data)<< " ";_InOrder(root->_right);}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR)subLR->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subL;}else{grandfather->_right = subL;}subL->_parent = grandfather;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL)subRL->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subR;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subR;}else{grandfather->_right = subR;}subR->_parent = grandfather;}}void Destroy(Node*root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;}
private:Node* _root = nullptr;
};
map/set的模拟实现
map的模拟实现:
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>::const_Iterator 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){pair<iterator, bool> it = _t.Insert(kv);return it;}void inorder(){_t.InOrder();}iterator find(const K&key){return _t.Find(key);}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert({ key,V() });return ret.first->second;}
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, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::const_Iterator 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);}void inorder(){_t.InOrder();}
private:RBTree<K, const K, SetKeyOfT> _t;
};
当我们将红黑树封装完成后,对于map/set的实现我们只需要封装它的接口就可以完成,不过值得注意的是,对于给红黑树所传的参数,第二个参数才是我们所要保存的真实值,为了让它不被修改,我们用const修饰。
对于迭代器的重定义,我们看到前面多了一个关键字typename,这个关键字是申明后面的参数是类模板,这样我们就可以给迭代器类重命名为iterator和const_iterator。
小结
本篇博客我们介绍了红黑树的封装以及map/set的模拟实现,同时介绍了一种巧妙的泛型思想,通过map/set的模拟实现让我们能更加清楚的了解map/set的运行逻辑及使用。
以上就是本篇博客的主要内容,如果对您有所帮助的话希望能够点赞关注转发加收藏,您的支持就是我创作的最大动力。