在我们了解了AVLTree与红黑树之后,我们便可以去理解set与map的底层了,stl中的set与map就是用红黑树去实现的,那么他们到底是如何实现的呢?我们一起来了解下
stl中的map与set源码框架
map框架
set框架
我们其实按照一般的想法去理解,set与map应该是分别使用一棵二叉树来进行底层实现,但其实不是的,因为那样就会引起代码的冗余,我们的map与set仅仅是模型的参数不同,set是<k,k>模型,map是<k,value>模型,也仅仅只有参数不同而已,那么它到底是如何实现一棵树用在两个模型上的呢?
我们来看看stl中是如何来解决这个问题的
首先我们在成员变量中引入了我们需要使用的红黑树模板,我们可以看到,在红黑树模板中,建立了一个value模型,通过成员变量rb_tree_node来控制我们的第二个参数value,当我们类型匹配到set时,set中的第二个参数就是key,当匹配到map时,rb_tree_node就决定map中的第二个参数变为了pair,所以其实决定set,map中模型类型的,是rb_tree_node去传模板参数来决定的
值得注意的是我们如此改变之后,红黑树模板也需要相应做出调整,就不再确定参数类型了,需要对红黑树进行改造
我们可以看到,我们在红黑树中加了一个模板参数,用这个模板参数来决定调用,在内部设置了一个仿函数,分别对map与set进行处理,set返回的k,map返回的kv.first,随后才对各自的操作进行处理
红黑树的迭代器
我们通过浏览源码可知,set与map的迭代器,实际上也都是红黑树完成的,所以我们这里对红黑树的迭代器进行研究
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:begin()与end()STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置
operator++()与operator--()
对于我们一般对于红黑树的认知,我们的++与--逻辑上是如何来走的呢,是不是如上图一样,5,6,7,8....一直到15,有序的排列呢?我们可以发现,8的下一个是它右子树的最左节点,8的上一个是左子树的最右结点,所以++我们可以将结点分为有右子树与没有右子树的,当一个结点有右子树时,其下一个节点就是右子树的最左节点,当一个结点没有右子树时,又会分为两种情况,在左树,则他的下一个节点他的parent,在右树,则他的下一个节点为parent的parent,7与8就是例子,由此我们可以得出代码
Self& operator++()//重载++
{
// 1、如果右不为空,中序的下一个就是右子树的最左节点
// 2、如果右为空,表示_node所在的子树已经访问完成,在一个节点在他的祖先中找
// 沿着路径往上找孩子是它的左的那个祖先
if (_node->_right)//右节点存在
{
Node* subLeft = _node->_right;//初始化subLeft
while (subLeft->_left)//当subLeft有左子节点时
{
subLeft = subLeft->_left;//向左走到最后一个叶子节点
}
_node = subLeft;//返回此节点
}
else//当右为空
{
Node* cur = _node;//初始化cur
Node* parent = cur->_parent;//初始化parent
while (parent && cur == parent->_right)//当parent存在,且cur为parent的右子树
{
cur = cur->_parent;向上走
parent = parent->_parent;
}
//此时代表左子树遍历完毕,该根结点了
_node = parent;//parent就是下一个节点,这种情况包含了cur为左子树的情况
}
return *this;//返回此节点
}
而我们的--,则是分为有无左子树,有左子树,则下一个就为左子树的最右结点,就像8下来是7一样,无左子树,又分为两种情况,为右树,则下一个直接就是parent,就像15与13一样,为左树,下一个就是parent的parent,就像12与11一样
Self& operator--()
{
if (_node->left)
{
Node* subRight = _node->left;
while (subRighr&&subRight->right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
Node* cur = _node;
Node* parent = _cur->_parent;
while (parent&&cur == parent->_left)
{
cur = cur->_parent;
parent = parent_parent;
}
_node = parent;
}
return *this;
}
当我们完成了最关键的++与--,我们就可以将其封装在迭代器中,我们也就可以去模拟实现set与map了
模拟实现set
namespace wxy
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& k)//仿函数,返回判断参数
{
return k;//这里返回k,代表k模型
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;//调用kk模型
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
bool Insert(const K& k)
{
return _t.Insert(k);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
void test_set()
{
set<int> s;
s.Insert(3);
s.Insert(4);
s.Insert(1);
s.Insert(2);
s.Insert(5);
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
}
模拟实现map
namespace wxy
{
template<class K, class V>
class map
{
struct MapKeyOfT//map的仿函数,用于传递pair参数
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;//反回的是kv的pair中的first
}
};
public:
typedef typename RBTree<K, pair<K,V>, MapKeyOfT>::iterator iterator;//调用kv模型
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
bool Insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
private:
RBTree<K, pair<K, V>, MapKeyOfT> _t;
};
void test_map()
{
map<int, int> m;
m.Insert(make_pair(1, 1));
m.Insert(make_pair(3, 3));
m.Insert(make_pair(10, 10));
m.Insert(make_pair(5, 5));
m.Insert(make_pair(6, 6));
map<int, int>::iterator it = m.begin();
while (it != m.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
for (auto kv : m)
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;
}
}
改造过后可以适配set与map的RBTree
enum Colour
{
BLACK,
RED,
};
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;//判断参数
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)//判断set还是map的参数
, _col(RED)
{}
};
template<class T>
struct __TreeIterator//红黑树迭代器
{
typedef RBTreeNode<T> Node;
typedef __TreeIterator<T> Self;
Node* _node;
__TreeIterator(Node* node)
:_node(node)
{}
T& operator*()//重载解引用
{
return _node->_data;//返回判断参数
}
T* operator->()
{
return &_node->_data;
}
Self& operator++()//重载++
{
// 1、如果右不为空,中序的下一个就是右子树的最左节点
// 2、如果右为空,表示_node所在的子树已经访问完成,在一个节点在他的祖先中找
// 沿着路径往上找孩子是它的左的那个祖先
if (_node->_right)//右节点存在
{
Node* subLeft = _node->_right;//初始化subLeft
while (subLeft->_left)//当subLeft有左子节点时
{
subLeft = subLeft->_left;//向左走到最后一个叶子节点
}
_node = subLeft;//返回此节点
}
else//当右为空
{
Node* cur = _node;//初始化cur
Node* parent = cur->_parent;//初始化parent
while (parent && cur == parent->_right)//当parent存在,且cur为parent的右子树
{
cur = cur->_parent;向上走
parent = parent->_parent;
}
//此时代表左子树遍历完毕,该根结点了
_node = parent;//parent就是下一个节点,这种情况包含了cur为左子树的情况
}
return *this;//返回此节点
}
Self& operator--()
{
if (_node->left)
{
Node* subRight = _node->left;
while (subRighr&&subRight->right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
Node* cur = _node;
Node* parent = _cur->_parent;
while (parent&&cur == parent->_left)
{
cur = cur->_parent;
parent = parent_parent;
}
_node = parent;
}
return *this;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
};
template<class K, class T, class KOfT>//改造后的红黑树
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __TreeIterator<T> iterator;
iterator begin()//迭代器的起始点,树的最左下角
{
Node* cur = _root;//初始化cur
while (cur && cur->_left)//当cur存在且cur的左子节点存在
{
cur = cur->_left;//向左移动,直至最后一个叶子节点
}
return iterator(cur);//返回cur
}
iterator end()//迭代器的末尾
{
//return iterator(nullptr);//返回NULL或者最右结点
Node* cur = _root;
while (cur && cur->_right)
{
cur = cur->_right;
}
return iterator(cur);
}
bool Insert(const T& data)
{
// 1、按搜索树的规则进行插入
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return true;
}
KOfT koft;//此参数随着set与map的不同,set会变为K去调data,map则会变为pair中first去调data
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (koft(cur->_data) < koft(data))
{
parent = cur;
cur = cur->_right;
}
else if (koft(cur->_data) > koft(data))
{
parent = cur;
cur = cur->_left;
}
else
//........