set和map的模拟实现
红黑树封装实现 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 tree
link_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 层分别实现一个 MapKeyOfT
和 SetKeyOfT
的仿函数传给 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
指向的结点的右子树为空,代表当前结点已经访问完了且当前结点所在的子树也访问完了,要访问的下一个结点在当前结点的祖先里面,所以要沿着当前结点到根的祖先路径向上找。
示例分析:
如果当前结点是父亲的左,根据中序左子树->根结点->右子树,那么下一个访问的结点就是当前结点的父亲;如上图: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 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;
}
}
else
{
// g
// u p
Node* 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
// c
if (cur == parent->_right)
{
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);
}
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 kot
Node* 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;
else
parent->_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;
};
}