我们想要实现STL中的set和map,那么第一步就需要看一下库函数是如何实现的:
通过查看源代码我们发现两个容器都包含了stl_tree.h
,因此我们猜测此头文件实现的是红黑树。
但是set和map很显然不是使用同一棵树实现的,那么STL库是怎么解决这个问题的呢?
我们发现在构造红黑树的value时map使用的是pair<Key,T>,而set使用的则是Key
但是为什么在rb_tree类的构造时,要传四个参数呢?
我们知道Value是数据域,那么为什么还有传递Key呢?这难道不是重复了吗?
剩下的两个参数又代表着什么意思呢?
接下来我们的任务就是解决上述的问题。
一、set 和 map 的插入
首先value在插入时非常完美,但是在查找和删除时我们又应如何操作呢?set实现时,可以通过Value来进行操作,但是map实现时,我们有应如何操作呢?提取first元素吗?这样的话传递一个Key类型来方便操作。
接下来我们来实现一下插入部分发现:
if (kv < cur->_kv)
{
parent = cur;
cur = cur->_left;
}
当我们进行到比较操作的修改时,这个数据如何比较呢?
set的话我们当然可以直接比较,但是map呢?
通过查阅我们发现不是仅仅按first比较,因此我们需要定义方法来解决这个问题。
我们如何按照我们所预想的情景比较呢?
这样的话我们可以定义一个类来取得想要比较的元素
因此插入部分代码修改如下:
bool Insert(const V& v)
{
//根节点为空,那此时插入的节点就是新节点
if (_root == nullptr)
{
_root = new Node(v);
_root->_col = Black;
return true;
}
KeyOfV kov;//提取key
//此时根节点不为空
//开始找应该插入的位置
Node* cur = _root;
Node* parent = cur->_parent;
while (cur)
{
if (kov(v) <kov( cur->_v))
{
parent = cur;
cur = cur->_left;
}
else if (kov(v) >kov( cur->_v))
{
parent = cur;
cur = cur->_right;
}
else
return false;//相等就表示已经存在了
}
//插入新节点
cur = new Node(v);
if (kov(v)<kov( parent->_v))
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
//红色才修改
while (parent&&parent->_col == Red)
{
//取得g节点和u节点
Node* grandfather = parent->_parent;
Node* uncle = nullptr;
//父节点此时为左孩子
if (parent == grandfather->_left)
{
uncle = grandfather->_right;
}
//父节点此时为右孩子
else
{
uncle = grandfather->_left;
}
//u节点为红色
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Black;
grandfather->_col = Red;
cur = grandfather;
parent = cur->_parent;
}
//u节点不存在或u节点为黑色
else
{
//父节点是g的左孩子
if (parent == grandfather->_left)
{
//cur是父节点的左孩子
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = Black;
grandfather->_col = Red;
}
//cur是父节点的右孩子
else
{
RotateLR(grandfather);
cur->_col = Black;
grandfather->_col = Red;
}
break;//调整完此时一定ok
}
//父节点是g的右孩子
else
{
//cur是父节点的左孩子
if (cur == parent->_left)
{
RotateRL(grandfather);
cur->_col = Black;
grandfather->_col = Red;
}
//cur是父节点的右孩子
else
{
RotateL(grandfather);
parent->_col = Black;
grandfather->_col = Red;
}
break;
}
}
}
_root->_col = Black;
return true;
}
二、set 和 map 迭代器实现遍历
我们通过查看源代码发现,map和set底层都是通过调用红黑树的迭代器来完成遍历的:
那么此时我们只需要关注红黑树的迭代器是怎么实现的即可。
而底层的红黑树实现是利用头结点:
我们没有设置头节点,因此在这里简单实现一下:
//红黑树迭代器
template<class V,class Ref,class Ptr>
struct RBTreeIterator
{
typedef typename RBTreeNode<V> Node;//节点
typedef typename RBTreeIterator<V, Ref, Ptr> Self;
Node* _node;
Node* _root;
RBTreeIterator(Node* node,Node* root)
:_node(node)
,_root(root)
{}
//++_node
Self& operator++()
{
Node* cur = _node;
if (cur->_right )
{
Node* LeftMost = cur->_right;
while (LeftMost->_left)
{
LeftMost = LeftMost->_left;
}
_node = LeftMost;
}
else
{
Node* parent = cur->_parent;
while (parent && parent->_right == cur)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
//--_node
Self& operator--()
{
if (_node == nullptr)
{
Node* RightMost = _root;
while (RightMost->_right)
{
RightMost = RightMost->_right;
}
_node = RightMost;
}
else
{
Node* cur = _node;
if (cur->_left)
{
Node* RightMost = cur->_left;
while (RightMost->_right)
{
RightMost = RightMost->_right;
}
_node = RightMost;
}
else
{
Node* parent = cur->_parent;
while (parent && parent->_left == cur)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
}
return *this;
}
Ref operator*()
{
return _node->_v;
}
Ptr operator->()
{
return &_node->_v;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
};