STL库的实现方式
map和set的底层用的红黑树是一样的吗?从容器特点的角度出发,这里个容器的底层应该分别用key搜索模型的红黑树和key value 搜索模型的红黑树。但是,从库的设计角度出发,这两者用同一份红黑树代码更好。而STL就是用的同样一份key value 搜索模型的红黑树进行对map和set的封装的。下面就通过STL库的源码来简单看一看。
可以看到,STL库里set也是用key vlaue搜索模型的红黑树。这颗红黑树的key存的是key,value存的也是key。下面再看看map。
通过上图可以看到,库里面的map的底层用的也是一样的红黑树。不同的是这颗红黑树的value存的是一个pair结构。为什么这么设计呢?那就再往深层次谈一谈,我们下面就看看库里面的红黑树是怎么设计的。
可以看到rb_tree是通过Value模版去实例化树的节点的。set传的Value是Key,底层__rb_tree_node类实例化的就是key的树。map传的Value是pair,底层__rb_tree_node类实例化的就是key value的树。这里的__rb_tree_node的就像一个适配器,上层传什么模板的Value,底层就对应实例化不同的节点。
虽然set和map的模板参数一样,都是Key Value。由于Value传的类型不一样,底层红黑树实例化也是不一样的。所以,本质上map和set用的不是同一颗红黑树,而是使用的同一份红黑树的模板。
那为什么还需要Key这个模板呢?在find和erase接口中还需要这个类型。
模拟实现map和set
我们现将红黑树的代码进行一下调整,将原来Key Value 的模板改成库里面的那样。然后类似于库里的map和set先把类模板和类成员基本的框架搭出来。
引入第三个模板参数来获取T的key
下面调整一下红黑树的插入接口。
通过上图看,在修改后的代码在比较逻辑上面会有问题。如果是map的话,这里的T就是一个pair,而pair的比较不符合实际用key来比较的需求。解决的方式就是用仿函数来处理,这里的仿函数和在优先级队列那里设计的方式又有区别。这里的仿函数用于将pair中key对象取出来。方便我们在需要用key进行比较的场景使用。
下面需要给红黑树再添加一个模板参数,然后分别在map和set内部实现一个仿函数
从这里就不得不佩服大佬设计数据结构的思路了。通过学习大佬前辈们在库里面的实现思路,不得不为大佬对于泛型编程水平折服。通过加一个模板,对map设计一个仿函数就可以达到通过T获取Key对象,这里set其实是被map稍稍拖了下后腿,不过问题不大。
迭代器的模拟实现
先以set为例讲解一下迭代器的实现思路,首先是迭代器的设计,这里使用T一个模板参数就能满足map和set的需求了。迭代器基础功能的实现这里不做过多讲解。重点需要理解的是operator++的重载。
上图中的set在用红黑树的迭代器时,用typename是告诉编译器这是一个类型。
为了让我们简易版本的迭代器能跑起来,还需要实现operator++的重载。根据搜索二叉树的性质可知,当树进行中序遍历时,值是有序的。那么起始位置就是最小的值所在的节点。其实就是从根节点出发,树的最左子树。
那如何访问下一个节点呢?这要分两类情况讨论。
情况一:当前节点右子树存在,就去访问右子树的最小节点。
情况二 当前节点的右子树为空,去访问孩子是父亲左边的祖先。
情况二又可以细分下面两种情况,分别是当前节点在父亲节点的左边和右边。若当前节点在父亲的左边,直接将修改当前节点值为当前节点的父节点即可。若当前节点是父亲的右子树,则需要向上遍历,直到找到当前节点是父亲节点的左子数时,修改当前节点的值即可。需要注意的是,当最大的值的节点访问完后,parent为空。所以,可以将end()的返回值设置成空。
参考代码如下
下面将operator–实现一下。其实operator–和operator++的实现起来大不差不差,两个大情况正好和operator++的互为镜像。下面就直接提供一份代码以供参考。
而这里的简易迭代器其实是不符合set的要求的,因为set的key值是不能被修改的。所以我们需要封装一个const迭代器,而库里面set的普通迭代器和const迭代器都是const迭代器。
这里顺便提一下map的普通 迭代器是如何支持修改Value而禁止修改Key的。map是通过模板T这个模板参数参数的pair<const K, Value>
将上面的代码进行一下调整,将迭代器的模板参数增加两个,用于适配出const迭代器。然后修改一下set的迭代器部分的内容。
接下来把map的部分写出来。
map的普通迭代器要求Key不能被修改,Value可以修改。将pair里的K用const修饰,这样就避免Key在使用普通迭代器的情况下被修改了。
map的operator[]的模拟实现
在前面的map/set使用的文章中介绍map的[]时提到,其实map的operator[]底层使用了insert接口进行实现的。
下面先将红黑树的insert接口进行一下修改。以符合我们的需求。
然后修改set的insert接口的时候,问题出现了。
这里由于set的iterator是const迭代器,insert返回的是普通迭代器。这里类型不匹配了。如何解决?先看一看库是怎么处理的。
然后,直接借鉴库的写法将接口修改一下。
根据库的写法修改后依旧还是有类型不匹配的问题。因为set的iterator是const迭代器,而insert的返回值是普通迭代器。要想能够匹配需要提供一个用普通迭代器构造const迭代器的接口。下面就看一看源码是如何实现的。
库里面的解决方案是提供了一个特殊的构造函数。当iterator被实例化成普通迭代器时,这就是普通迭代器的拷贝构造。当iterator被实例化成const迭代器时,这个函数就会变成支持普通迭代器构造const迭代器的构造函数。这样上面set关于返回值的问题就能够得以解决了。
解决了这个问题,那就将operator[]手撕出来map/set的封装的关键知识学习就差不多了。operator[]实现思路如下,通过复用insert[]来获取key所在的节点的pair。然后,取出pair的迭代器并使用operator->访问对应的Value即可。
总结
通过使用红黑树封装map/set,可以加深我们对于容器的理解。通过模拟实现set/map的关键部分,学习到了大佬在设计库的思想。一份红黑树模板可以实例化成两份不一样的容器。由于map和set性质上的差异,相应的红黑树的某些接口如,insert需要进行相应的调整。在迭代器部分设计的特殊的构造函数是非超巧妙的,让set的insert为了兼容map的insert的返回普通迭代器时,用这个普通迭代器构造成const迭代器,符合set的设计。
完整参考代码
//set
#pragma once
#include "RBTree.h"
namespace xyx
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
const_iterator begin() const
{
return _tree.begin();
}
const_iterator end() const
{
return _tree.end();
}
pair<iterator, bool> insert(const K& key)
{
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> p = _tree.Insert(key);
return pair<iterator, bool>(p.first, p.second);
}
private:
RBTree<K, K, SetKeyOfT> _tree;
};
}
//map
#pragma once
#include "RBTree.h"
namespace xyx
{
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 _tree.begin();
}
iterator end()
{
return _tree.end();
}
const_iterator begin() const
{
return _tree.begin();
}
const_iterator end() const
{
return _tree.end();
}
V& operator[](const K& key)
{
pair<iterator, bool> p = insert(make_pair(key, V()));
return p.first->second;
}
pair<iterator, bool> insert(const pair<K, V> kv)
{
return _tree.Insert(kv);
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _tree;
};
}
//RBTree
#pragma once
#include <utility>
enum COLOR
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode(const T& data)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
, _data(data)
,_color(RED)
{}
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
COLOR _color;
};
template<class T, class Ptr, class Ref>
struct __TreeIterator
{
typedef RBTreeNode<T> Node;
typedef __TreeIterator<T, T*, T&> iterator;
typedef __TreeIterator<T, Ptr, Ref> Self;
Node* _node;
__TreeIterator(const iterator& it)
:_node(it._node)
{}
__TreeIterator(Node* root)
:_node(root)
{}
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--(int)
{
Self tmp = *this;
//左子树不为空,访问左子树最右节点(最大节点)
if (_node->_left)
{
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else //左子树为空,访问孩子是父亲右的祖先节点
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return tmp;
}
Self& operator--()
{
//左子树不为空,访问左子树最右节点(最大节点)
if (_node->_left)
{
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else //左子树为空,访问孩子是父亲右的祖先节点
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator++()
{
//右子树不为空,访问右子树最左节点(最小节点)
if (_node->_right)
{
Node* subleft = _node->_right;
while (subleft->_left)
{
subleft = subleft->_left;
}
_node = subleft;
}
else //右子树为空,访问孩子是父亲左的祖先节点
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent)
{
if (cur == parent->_left)
{
break;
}
else
{
cur = parent;
parent = parent->_parent;
}
}
_node = parent;
}
return *this;
}
};
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __TreeIterator<T, T*, T&> iterator;
typedef __TreeIterator<T, const T*, const T&> const_iterator;
const_iterator begin() const
{
//树的最左节点(最小节点)
Node* leftmin = _root;
while (leftmin && leftmin->_left)
{
leftmin = leftmin->_left;
}
return const_iterator(leftmin);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
iterator begin()
{
//树的最左节点(最小节点)
Node* leftmin = _root;
while (leftmin && leftmin->_left)
{
leftmin = leftmin->_left;
}
return iterator(leftmin);
}
iterator end()
{
return iterator(nullptr);
}
RBTree()
:_root(nullptr)
{}
Node* find(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->data) < key)
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->data) > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
pair <iterator, bool> Insert(const T& data)
{
//第一次插入数据
if (_root == nullptr)
{
//根节点只能是黑色
_root = new Node(data);
_root->_color = BLACK;
return make_pair(iterator(_root), true);
}
//找到合适的位置插入
Node* parent = nullptr;
Node* cur = _root;
KeyOfT kot;
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
{
//key已经存在
return make_pair(iterator(cur), false);
}
}
//走到这里表示找到合适的位置插入
cur = new Node(data);
Node* newnode = cur;
cur->_color = RED;//新节点为红色
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//红黑树的控制逻辑
//父亲节点的颜色为黑色不需要调整
//反之,需要调整
while (parent && parent->_color == RED)
{
Node* grandFather = parent->_parent;
if (parent == grandFather->_left)//考虑父亲在祖父的位置
{
Node* uncle = grandFather->_right;
if (uncle && uncle->_color == RED)//uncle 存在且为红
{
//将uncle和parent改成黑色,grandfather改成红色
uncle->_color = parent->_color = BLACK;
grandFather->_color = RED;
//如果祖父就是根节点,将祖父修改成黑色,退出即可
if (grandFather == _root)
{
grandFather->_color = BLACK;
break;
}
else //然后继续向上迭代处理
{
cur = grandFather;
parent = cur->_parent;
}
}
else //uncle不存在 或者 uncle存在且为黑
{
if (parent->_left == cur) //单旋的场景
{
RotateR(grandFather);
parent->_color = BLACK;
grandFather->_color = RED;
}
else // LR双旋转
{
RotateL(parent);
RotateR(grandFather);
cur->_color = BLACK;
grandFather->_color = RED;
}
break; //旋转后直接退出即可
}
}
else //parent == grandFather->_right
{
Node* uncle = grandFather->_left;
if (uncle && uncle->_color == RED) //uncle 存在且为红
{
uncle->_color = parent->_color = BLACK;
grandFather->_color = RED;
//如果祖父就是根节点,将祖父修改成黑色,退出即可
if (grandFather == _root)
{
grandFather->_color = BLACK;
break;
}
else //然后继续向上迭代处理
{
cur = grandFather;
parent = cur->_parent;
}
}
else //uncle不存在 或者 uncle存在且为黑
{
if (parent->_right == cur) //单旋的场景
{
RotateL(grandFather);
parent->_color = BLACK;
grandFather->_color = RED;
}
else // RL双旋转
{
RotateR(parent);
RotateL(grandFather);
cur->_color = BLACK;
grandFather->_color = RED;
}
break; //旋转后直接退出即可
}
}
}
return make_pair(iterator(newnode), true);
}
void RotateL(Node* parent)
{
//_rotateCount++;
//将curLeft连接到parent的右子树中
//将parent连接到cur的左子树中
//修改cur、parent、curLeft的_parent
Node* cur = parent->_right;
Node* curLeft = cur->_left;
parent->_right = curLeft;
// curLeft可能为空
if (curLeft)
curLeft->_parent = parent;
cur->_left = parent;
//提前保存parent的_parent避免节点丢失
Node* ppnode = parent->_parent;
parent->_parent = cur;
//边界情况,修改_root
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
ppnode->_left = cur;
else
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
//++_rotateCount;
//将cur的右子树连接到parent的左子数
//让parent连接到cur的右子树
//修改parent、cur、curRight的_parent
Node* cur = parent->_left;
Node* curRight = cur->_right;
parent->_left = curRight;
//curRight可能为空
if (curRight)
curRight->_parent = parent;
//避免parent->_parent节点丢失
Node* ppnode = parent->_parent;
parent->_parent = cur;
cur->_right = parent;
//边界情况
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
ppnode->_left = cur;
else
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
bool checkcolor(Node* root, int blacknum, int benchmark)
{
//处理边界情况
if (root == nullptr)
{
//走到NIL节点,基准值与当前路径黑节点数量不一致
if (blacknum != benchmark)
return false;
return true;
}
//统计当前路径的黑节点数量
if (root->_color == BLACK)
++blacknum;
//出现连续红节点的情况
if (root->_color == RED && root->_parent && root->_parent->_color == RED)
{
cout << root->_kv.first << "-> 位置出现连续红节点" << endl;
return false;
}
//子问题分治
return checkcolor(root->_left, blacknum, benchmark) && checkcolor(root->_right, blacknum, benchmark);
}
bool IsBalance()
{
return IsBalance(_root);
}
bool IsBalance(Node* root)
{
//处理边界情况
if (root == nullptr)
return true;
//根节点只能为黑色
if (_root->_color == RED)
{
return false;
}
//以最左路径的黑节点的数量为基准值
int benchmark = 0;
Node* cur = root;
while (cur)
{
if(cur->_color == BLACK)
++benchmark;
cur = cur->_left;
}
return checkcolor(root, 0, benchmark);
}
int Height()
{
return Height(_root);
}
int Height(Node* root)
{
//处理边界情况
if (root == nullptr)
return 0;
int leftH = Height(root->_left);
int rightH = Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
private:
Node* _root;
//public:
// int _rotateCount = 0;
};