一、利用模板区分map和set
// 节点的颜色
enum Colour
{
RED,
BLACK
};
// 红黑树节点的定义
// T是值的类型: 如果是map,则为pair<K, V>; 如果是set,则为k
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)//data可能是K也可能是<K.V>
,_col(RED)
{}
};
由于map和set的valuetype不同,我们将红黑树的模板参数改为T,T代表的就是valuetype,来区别set和value的数据类型。
二、set和map的迭代器
2.1红黑树的begin()和end()
STL库中源码是采用下图结构,用了应该header节点,迭代器begin()可以指向header的左,迭代器end()指向header。
本文采用的是无头节点进行封装,用nullptr作为end()。
iterator begin()//找到最左节点的迭代器就是begin()
{
Node* leftMin = _root;
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return iterator(leftMin);
}
iterator end()
{
return iterator(nullptr);
}
//重载begin和end函数,便于const迭代器使用
const_iterator begin() const//找到最左节点的迭代器就是begin()
{
Node* leftMin = _root;
while (leftMin && leftMin->_left)
{
leftMin = leftMin->_left;
}
return const_iterator(leftMin);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
这里begin()迭代器返回的就是最左节点,end()迭代器我们使用nullptr,重载const_iterator是为了set和map的const对象使用
2.2 operator++()
这里重载++分俩种情况,一种是右树存在,一种是右树不存在。
- 右树存在的话,++就要找到比_node大的最小的数,也就是_node的右子树的最左边的数。
- 右树不存在的话,说明要往parent找,如果_node在parent左边,那下一个比_node大的最小数就是parent。
如果_node在parent右边,说明_node比parent大,就需要继续往上找,找到孩子是父亲左的那个父亲节点,就是下一个要访问的节点。
因为_node在parent左边,parent肯定比_node大,这个parent就是比_node大的最小的数。
总结就是遍历顺序:左子树 根 右子树
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 && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
2.3 operator–()
同理,–也是分俩种情况,_node左树存在和左树不存在
- _node左树存在,我们就找到左树的最右边的节点
- _node左树不存在,就找到孩子是父亲右的那一个节点,返回父亲,走到nullptr就是遍历完成。
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;
}
三、set迭代器
set的value是不可以修改的,所以它的底层将普通迭代器和const迭代器全部封装成const_iterator。
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
//set的普通迭代器和const迭代器其中都是同一个类型,所以我们只需要const迭代器的begin和end
//对应的,我们要在红黑树当中补齐const_iterator迭代器。
const_iterator begin() const
{
return _t.begin();
}
const_iterator end() const
{
return _t.end();
}
四、map迭代器
map的key不能修改,但是value是可以修改的,这里STL用了一种很巧妙的方法,就是在利用红黑树封装map的时候我们对传入的pair的K用const进行修饰,这样,我们就可以保证K不被修改,而value是可以被修改的。
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
//这里俩个迭代器中都将K进行const再传入,这样就不能修改key,但可以修改value
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 _t.begin();
}
iterator end()
{
return _t.end();
}
const_iterator begin() const
{
return _t.begin();
}
const_iterator end() const
{
return _t.end();
}
insert函数用pair<iterator,bool>作为返回值类型,RBtree中insert函数,如果要插入的数据存在,就会返回已经存在迭代器的位置和false,如果不存在就会返回新插入节点的位置和true。
重载[]:借助了insert函数,我们将需要的key传入,就可以返回key位置的迭代器,再利用迭代器就可以找到key的value
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
//这里的迭代器就是普通迭代器,不需要像set再用一个ret来接收
pair<iterator,bool> insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
五、用普通迭代器构造const迭代器
set中的inset函数:
由于set的iterator是const迭代器,所以我们不能直接用rbtree的Insert进行返回,我们要先利用一个普通迭代器来接收它,再利用普通迭代器构造const迭代器的构造函数,返回set中的insert函数。
//这里的iterator是RBTree::const_iterator
//用普通迭代器先接收,再用普通迭代器构造const迭代器const_iterator
pair<iterator,bool> insert(const K& key)
{
// pair<RBTree::iterator, bool>//这里的iterator是RBTree中的普通迭代器
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
return pair<iterator, bool>(ret.first, ret.second);
}
__TreeIterator(const Iterator& it)
:_node(it._node)
{}
在struct __TreeIterator中增加了这个拷贝构造函数
当Self中的Ptr和Ref被实例化成T和T&的时候,我们这个函数就是普通的拷贝构造
当Self中的Ptr和Ref被实例化成const T和const T&的时候,我们这个函数就是用普通迭代器构造const迭代器的构造,因为typedef __TreeIterator<T, T*, T&> Iterator;这里的Iterator永远是一个普通迭代器,作为构造函数的参数类型。
struct __TreeIterator
{
typedef RBTreeNode<T> Node;
typedef __TreeIterator<T, Ptr, Ref> Self;
//这里的迭代器直接用T*和T&传入,也就是说不管实例化出来的是const迭代器还是普通迭代器,这里都是普通迭代器,用来下面的构造函数传参
typedef __TreeIterator<T, T*, T&> Iterator;
//这个类被实例化成const迭代器时,这个函数是一个构造,支持普通迭代器构造const迭代器
//这个类被实例化成普通迭代器,这个函数就是一个拷贝构造
__TreeIterator(const Iterator& it)//这里的Iterator是普通迭代器
:_node(it._node)
{}
Node* _node;
__TreeIterator(Node* node)
:_node(node)
{}
六、利用仿函数类进行比较大小
set仿函数类:
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
map仿函数类:
struct MapKeyOfT
{
//这个函数是重载括号运算符,返回key,便于插入的时候进行比较key再插入
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
这里仿函数类重载了(),set的data就是key,主要是给map使用,map只要把kv的first返回即可,就是key。
七、源码
7.1 RBTree
// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<K, V>, MapKeyOfT> _t;
template<class K, class T,class KeyOfT>
//K还有什么用呢?用来作在find函数中的参数类型
class RBTree
{
typedef RBTreeNode<T> Node;
public:
//同一个类模板,传的不同的参数实例化出的不同类型
typedef __TreeIterator<T, T*, T&> iterator;
typedef __TreeIterator<T, const T*,const T&> const_iterator;
//查看节点是否存在,利用KeyOfT 伪函数类
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if(kot(cur->_data) > key)
{
cur = cur->_left;
}
else
{
return key;
}
}
return nullptr;
}
pair<iterator,bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root), true);
}
Node* parent = nullptr;
Node* cur = _root;
//找到要插入节点的parent,如果节点已经存在,返回已经存在节点的迭代器,和false
KeyOfT kot;//利用KeyOfT类中的()重载找到key
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), false);//要插入的数据已经存在,返回已经存在的数据迭代器和false
}
}
cur = new Node(data);
cur->_col = RED;
//根据新节点key的大小插入新节点
Node* newnode = cur;
if (kot(parent->_data) < kot(data))
{
parent->_right = newnode;
}
else
{
parent->_left = newnode;
}
newnode->_parent = parent;
//以上完成插入操作
//下面完成红黑树规则
while (parent && parent->_col == RED)
{
// 注意:grandfather一定存在
// 因为parent存在,且不是黑色节点,则parent一定不是根,则其一定有双亲
Node* grandfather = parent->_parent;
// 先讨论左侧情况
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// 情况一:叔叔节点存在,且为红
if (uncle && uncle->_col == RED)
{
//变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else// 情况二:叔叔节点不存在,或者叔叔节点存在且为黑
{
// g
// p
// c
if (cur == parent->_left)//左左
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else//左右
{
// g
// p
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;//
}
}
// 再讨论右侧情况
else//parent == grandfather->_right
{
Node* uncle = grandfather->_left;
//情况一:叔叔节点存在,且为红
if (uncle && uncle->_col == RED)
{
//变色
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续向上调整
cur = grandfather;
parent = cur->_parent;
}
// 情况二:叔叔节点不存在,或者叔叔节点存在且为黑
else
{
if (cur == parent->_right)//右右
{
// g
// p
// c
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else//右左
{
// g
// p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode), true);;
}
void RotateL(Node* parent)
{
++_rotateCount;
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (parent == ppnode->_left)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
++_rotateCount;
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
Node* ppnode = parent->_parent;
cur->_right = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (parent == ppnode->_left)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
}
//blacknum用来记录每条路径黑色节点的个数,这里不要传引用,这里才能记录每条路径的数量
bool CheckColour(Node* root, int blacknum, int benchmark)
{
//走到null之后,判断blacknum和benchmark是否相等
if (root == nullptr)
{
if (blacknum != benchmark)//root等于空说明已经到路径的尾部,如果和基准值不一致,说明不是红黑树(每条线路的黑色节点相等)
return false;
return true;
}
if (root->_col == BLACK)
{
++blacknum;//记录黑色节点的个数
}
//检测当前节点与其双亲是否都为红色
//当前节点为颜色为红色且父亲存在且父亲颜色为红色,说明出现连续红色节点,返回false
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << root->_kv.first << "出现连续的红色节点,违反性质三" << endl;
return false;
}
return CheckColour(root->_left, blacknum, benchmark)
&& CheckColour(root->_right, blacknum, benchmark);
}
bool IsBalance()
{
return IsBalance(_root);
}
bool IsBalance(Node* root)
{
// 空树也是红黑树
if (root == nullptr)
return true;
// 检测根节点是否满足情况,根节点不是黑色节点,直接返回false
if (root->_col != BLACK)
return false;
//设置一个基准值,获取任意一条路径中黑色节点的个数
//直接算最左边的路径方便,不管基准值是不是正确的后面只要有一条路径和基准值不相等,说明不满足红黑树条件
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
++benchmark;
cur = cur->_left;
}
// 检测是否满足红黑树的性质
return CheckColour(root, 0, benchmark);//检查每条路径的黑色节点数量是否相等
}
int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = Height(root->_left);
int rightHeight = Height(root->_right);
return leftHeight > rightHeight ? leftHeight+1 : rightHeight+1;
}
private:
Node* _root = nullptr;
public:
int _rotateCount = 0;
};