一、map和set的源码和框架分析
我们前面学习map和set的使用的时候,知道set是对应我们的key问题,然后map对应的是我们的key和value问题,所以这两者存储的数据是不一样的,然后其二者的底层又是使用的红黑树实现的,那么我们是否需要去对应其存储的数据的特点,然后分别弄一个红黑树呢?
下面我们看看源码中是如何操作的:
set.h:
map.h:
可以看到源码中,set和map是用的同一个红黑树。
不过set中的红黑树的pair存储的数据是这样的:pair<K,K>。其让两个数据都存储key。
然后map中的pair就一个存储key一个存储value。
二、实现的基本框架
首先是我们的红黑树的框架,我们通过模板参数来控制。
我们节点存储的数据通过模板控制一个泛型,那么其可以传入一个pair类。
然后我们的树类,我们就使用两个模板参数,这是为了在一些成员函数需要单独使用key的时候使用的。
那么我们来看看set的结构:
可以看到我们在set中的红黑树的结点创建中传入的是两个参数,都是K。
下面是map的结构:
我们在map结构中的第二个模板参数传入的是一个pair。
三、insert插入的比较逻辑
我们知道无论是啥情况,我们的数据插入要比较的是关键码key。那么我们在插入函数中要如何进行比较呢?
首先就是我们的pair中是否有提供比较的逻辑,那么我们去库中看看:
复制代码
// 小于操作符 < 的实现
template<class T1, class T2>
template<class U1, class U2>
bool pair<T1, T2>::operator<(const pair<U1, U2>& p) const {
// 先比较 first,如果 first 不同,直接返回结果
if (first < p.first) return true;
if (p.first < first) return false;
// first 相等时,再比较 second
return second < p.second;
}
// 等于操作符 == 的实现
template<class T1, class T2>
template<class U1, class U2>
bool pair<T1, T2>::operator==(const pair<U1, U2>& p) const {
return first == p.first && second == p.second;
}
// 不等于操作符 != 的实现
template<class T1, class T2>
template<class U1, class U2>
bool pair<T1, T2>::operator!=(const pair<U1, U2>& p) const {
return !(*this == p);
}
// 大于操作符 > 的实现
template<class T1, class T2>
template<class U1, class U2>
bool pair<T1, T2>::operator>(const pair<U1, U2>& p) const {
return p < *this;
}
// 小于等于操作符 <= 的实现
template<class T1, class T2>
template<class U1, class U2>
bool pair<T1, T2>::operator<=(const pair<U1, U2>& p) const {
return !(p < *this);
}
// 大于等于操作符 >= 的实现
template<class T1, class T2>
template<class U1, class U2>
bool pair<T1, T2>::operator>=(const pair<U1, U2>& p) const {
return !(*this < p);
}
可以看到在库中pair的比较大小其实不适合我们红黑树中所需要的比较逻辑。
而且还有一个问题就是我们的红黑树是泛型实现的,那么存储的数据可能也不是pair,那么我们就要想个更好的方法了。
所以我们前面学习的仿函数在此处就起作用了,我们可以在set和map中分别实现一个SetKeyOfT、MapKeyOT。然后在RBTree中再添加一个模板参数KeyOfT仿函数,用来取出T类型对象中的key,然后进行比较。
在我们的红黑树的模板参数中添加多了一个仿函数模板参数。
然后我们在set和map的封装中,对于树的实例化中,我们将其对应的函数传入。
然后我们在红黑树中,对于数据的比较逻辑就可以通过仿函数提取出存储在pair中的key来进行比较了。
如上所示,通过实例化出仿函数的实例化对象,那么就可以使用其取得key了。
三、set和map迭代器的实现
iterator的实现思路大体上和list的iterator的是一致的,然后我们用一个类封装节点的指针,然后通过运算符重载的形式实现。
我们这里的迭代器的难点是operator++和operator--的实现。前面我们学习map和set的使用的时候,知道map和set的遍历走的是中序遍历:左根右的顺序,然后其begin()返回的是中序遍历的第一个节点的迭代器。
我们可以先在一个子树中来看迭代器++操作的逻辑,我们只考虑在一个子树中++,要如何找下一个节点。
迭代器++时,如果it指向的结点的右⼦树不为空,代表当前结点已经访问完了,要访问下⼀个结点 是右⼦树的中序第⼀个,⼀棵树中序第⼀个是最左结点,所以直接找右⼦树的最左结点即可。
如上图,我们访问10的时候,其右边子树是非空的,那么我们就去右边子树找最小的,那么其最小的在最左边。
迭代器++时,如果it指向的结点的右⼦树空,代表当前结点已经访问完了且当前结点所在的⼦树也 访问完了,要访问的下⼀个结点在当前结点的祖先⾥⾯,所以要沿着当前结点到根的祖先路径向上 找
那么此时我们it在25这个节点的话,那么其右边是空的,那么就表示我们当前节点的这个子树已经遍历完了,那么就要往根去走了。
如果当前结点是⽗亲的左,根据中序左⼦树->根结点->右⼦树,那么下⼀个访问的结点就是当前结 点的⽗亲;如下图:it指向25,25右为空,25是30的左,所以下⼀个访问的结点就是30。
如果当前结点是⽗亲的右,根据中序左⼦树->根结点->右⼦树,当前结点所在的⼦树访问完 了,当前结点所在⽗亲的⼦树也访问完了,那么下⼀个访问的需要继续往根的祖先中去找,直到找 到孩⼦是⽗亲左的那个祖先就是中序要问题的下⼀个结点。如下图:it指向15,15右为空,15是10 的右,15所在⼦树话访问完了,10所在⼦树也访问完了,继续往上找,10是18的左,那么下⼀个 访问的结点就是18。
如果当前结点是⽗亲的右,根据中序左⼦树->根结点->右⼦树,当前当前结点所在的⼦树访问完 了,当前结点所在⽗亲的⼦树也访问完了,那么下⼀个访问的需要继续往根的祖先中去找,直到找 到孩⼦是⽗亲左的那个祖先就是中序要问题的下⼀个结点。如下图:it指向15,15右为空,15是10 的右,15所在⼦树话访问完了,10所在⼦树也访问完了,继续往上找,10是18的左,那么下⼀个 访问的结点就是18。
end()如何表⽰呢?如下图:当it指向50时,++it时,50是40的右,40是30的右,30是18的右,18 到根没有⽗亲,没有找到孩⼦是⽗亲左的那个祖先,这是⽗亲为空了,那我们就把it中的结点指针 置为nullptr,我们⽤nullptr去充当end。需要注意的是stl源码空,红⿊树增加了⼀个哨兵位头结点 做为end(),这哨兵位头结点和根互为⽗亲,左指向最左结点,右指向最右结点。相⽐我们⽤ nullptr作为end(),差别不⼤,他能实现的,我们也能实现。只是--end()判断到结点时空,特殊处 理⼀下,让迭代器结点指向最右结点。具体参考迭代器--实现。
迭代器--的实现跟++的思路完全类似,逻辑正好反过来即可,因为他访问顺序是右⼦树->根结点-> 左⼦树,具体参考下⾯代码实现。
然后要注意的是map和set的迭代器都不支持修改。
代码如下:
四、operator[]
前面我们学习map的时候知道map支持[]实现数据的插入和修改的操作。那么我们的插入的话,就是复用我们的insert接口了。
\]的话其工作规则如下:
如果插入成功,那么返回的是一个pair的数据\<插入数据对应的迭代器,true\>。
如果插入失败,那么返回\<返回已经存在的数据对应的迭代器,false\>。
所以前面我们实现的红黑树中,其底层存储的数据类型我们时候pair也是有原因的。
## 五、完整代码
#pragma once
#include
#include
//RBTree.h
using namespace std;
enum color
{
Red,
Black
};
template
struct RBTreeNode
{
T _date;
RBTreeNode* _parent;
RBTreeNode* _left;
RBTreeNode* _right;
color _co;
RBTreeNode(const T&date)
:_date(data),
_parent(nullptr),
_left(nullptr),
_right(nullptr),
_co(Red)
{
}
};
template
struct TreeIterator
{
typedef RBTreeNode Node;
typedef TreeIterator Self;
Node* _node;
TreeIterator(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;
}
Self& operator--()
{
// ...
return *this;
}
Self& operator++()
{
// 当前节点右不为空,下一个就是右子树的中序第一个(最左节点)
// 当前节点右为空,下一个孩子是父亲左的那个祖先节点
if (_node->_right)
{
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
//此处的K是key,T是要存储的数据,这是为了map和set都可以用
template
class RBTree
{
typedef RBTreeNode Node;
public:
typedef TreeIterator Iterator;
typedef TreeIterator ConstIterator;
Iterator Begin()
{
Node* min = _root;
while (min && min->_left)
{
min = min->_left;
}
return Iterator(min);
}
Iterator End()
{
return Iterator(nullptr);
}
pair Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return { Iterator(_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 { Iterator(cur), 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;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 叔叔存在且为红->变色
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者叔叔存在且为黑->旋转+变色
{
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 // grandfather->_right == parent
{
// 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 { Iterator(newnode), true };
}
Node* Find(const K& key)
{
KeyOfT kot;
Node* cur = _root;
while (cur)
{
if (kot(cur->data) < key)
{
cur = cur->_right;
}
else if (kot(cur->data) > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
private:
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 (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
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 (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
private:
Node* _root = nullptr;
};
#pragma once
#include"RBTree.h"
//myset.h
namespace cyy
{
template
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree::Iterator iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
pair Insert(const T& key)
{
return _t.Insert(key);
}
private:
RBTree_t;
};
}
#pragma once
#include"RBTree.h"
//mymap.h
namespace cyy
{
template
class map
{
struct MapKeyOfT
{
const K& operator()(const pair& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree, MapKeyOfT>::Iterator iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
pair Insert(const pair& kv)
{
return _t.Insert(kv);
}
V& operator[](const K& key)
{
pair ret = insert({ key, V() });
return ret.first->second;
}
private:
RBTree,MapKeyOfT>_t;
};
}