目录
引言
改造比较
1.颜色定义
2.节点构造
3.迭代器
3.1 迭代器的模板参数
3.2 迭代器的构造函数
3.3 迭代器的运算符重载函数
3.3.1 前置++
3.3.2 前置--
3.3.3 源码对比
3.3.4 其它运算符重载
4.红黑树改造
4.1 红黑树的begin(),end()函数
4.2 红黑树的模板参数
4.3 下标访问
引言
在上篇中我们已经实现了红黑树的构造,但是和stl库中的红黑树还是有区别,这节我们将根据stl库
中的源码进行比对,改造红黑树,之后将用改造后的红黑树,来封装map和set,完成模拟实现
改造比较
1.颜色定义
第一个在颜色定义上,我们采用的是枚举的方法,而源码采用的是直接全局定义,不过两种方式本
质没什么区别,可以不需要改动
//源码
typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rb_tree_red = false;
const __rb_tree_color_type __rb_tree_black = true;
//我们自己的代码
enum Colour {
RED = 0,
BLACK,
};
2.节点构造
其次是节点的构造,我们在模拟实现红黑树的时候,采用的是kv型模板,即构造二叉树结点,都是
用pair来进行构造,但是我们需要用改造后的红黑树来封装map和set,也就是套用同一个红黑树模
板,前者的value才是pair,后者的value则为key
因此,我们要进行模板参数的改造,将kv模型改为T模板,由上层来实例化相应的红黑树
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)
, _col(RED)
{}
};
当然,源码会比我们的节点类写得更为精妙,源码与节点相关的类有两个类,一个是节点基类,另
一个才是节点类(节点基类的派生类),并且在节点基类中,加入了minimum,maximum两个函数,
之后在实现像是leftmost函数(找最左节点,即红黑树的最小节点中直接赋用即可)
//源码
struct __rb_tree_node_base
{
//重命名节点基类颜色为color_type
typedef __rb_tree_color_type color_type;
//重命名节点指针为base_ptr
typedef __rb_tree_node_base* base_ptr;
color_type color;
base_ptr parent;
base_ptr left;
base_ptr right;
//找最左节点,返回节点指针
static base_ptr minimum(base_ptr x)
{
while (x->left != 0) x = x->left;
return x;
}
//找最右节点,返回节点指针
static base_ptr maximum(base_ptr x)
{
while (x->right != 0) x = x->right;
return x;
}
};
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
//节点的成员仅有value_field,与我们的data成员是等价的
//传pair,实例化出的就是map;传key,实例化出的就是set
Value value_field;
};
3.迭代器
在之前红黑树的实现中,我们并没有实现红黑树的迭代器,而实际上,map和set都是支持双向迭
代器,即可以通过迭代器,实现中序遍历访问,因此,我们还需要在红黑树改造中,完成对迭代器
的实现
在链表那节中,我们已经提到过,迭代器要么是原生指针,要么是自定义类型对原生指针的封装,
模拟指针的行为,红黑树的迭代器也不例外,它实际就是原生指针的封装,然后在自定义类型中加
入相应前置++,前置--等函数
3.1迭代器的模板参数
和list类的迭代器相同,模板参数有三个,分别是T(Value),Ref,Ptr
其中后两个模板参数发挥的作用和list迭代器发挥的作用是相同的,目的就是完成const迭代器和普
通迭代器的实例化用同一个模板
当我们不想修改节点的值,需要使用const迭代器时,Ref模板参数开始发挥作用,只要往里面传
入const Value&即可实例化出相应的const迭代器
同理,假如树节点中存的是自定义类型,我们想要通过迭代器重载->来模拟实现用迭代器访问自定
义类型数据,Ptr模板参数开始发挥作用,只要往里面传入const T*或T*,即可分别实例化出不能通
过->修改节点中的数据的const迭代器和能通过->修改节点中的数据的普通迭代器
当然stl源码中,同样采取了迭代器基类,迭代器类(继承迭代器基类)的方式
基类包含了两个函数,一个是increment自增函数,另一个是decrement自减函数,两者都是服务
于后面迭代器前置++,--运算符重载
3.2 迭代器的构造函数
这里先提前说明,set类是不能对value进行修改的,而map类键值对应,是可以对value进行修改的
因此我们仿照源码中,模拟实现set和map封装时,set中的普通迭代器和const迭代器,实际上都是
const迭代器,不过这会导致上层红黑树类在调用begin函数时,发生冲突
因为set类的成员变量是普通对象_t,即便我们在红黑树类中已经实现了const迭代器版本的begin和end函数,但是在set类中,编译器依旧会调用普通迭代器的begin与end函数
//源码中set类的成员变量
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing set
//源码中红黑树类有关begin,end函数的实现
link_type& leftmost() const { return (link_type&) header->left; }
link_type& rightmost() const { return (link_type&) header->right; }
iterator begin() { return leftmost(); }
const_iterator begin() const { return leftmost(); }
iterator end() { return header; }
const_iterator end() const { return header; }
因此,在迭代器构造函数中,除了默认构造外,还要增加普通迭代器转为const迭代器的构造函数
//自己模拟实现
__RBtree_iterator() {}
__RBtree_iterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator<T, T&, T*> iterator; 拷贝构造
// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
// 支持普通迭代器构造const迭代器的构造函数
__RBtree_iterator(const __RBtree_iterator<T, T&, T*>& it)
:_node(it._node)
{}
//源码实现
typedef __rb_tree_iterator<Value, Value&, Value*> iterator;
typedef __rb_tree_iterator<Value, const Value&, const Value*> const_iterator;
//迭代器自身重命名
typedef __rb_tree_iterator<Value, Ref, Ptr> self;
//节点指针重命名
typedef __rb_tree_node<Value>* link_type;
__rb_tree_iterator() {}
__rb_tree_iterator(link_type x) { node = x; }
//参数是普通迭代器引用,当传入普通迭代器,它是拷贝构造;
//当传入const迭代器,它是支持普通迭代器构造const迭代器的构造函数
__rb_tree_iterator(const iterator& it) { node = it.node; }
3.3 迭代器的运算符重载函数
3.3.1 前置++
运算符重载函数中最关键就是实现前置++,--这两个运算符重载
对于红黑树来说,迭代器的移动要求符合中序遍历的要求,即用迭代器遍历整棵树时,会得到有
序序列
思路如下:
假如当前节点的右子树不为空,下一个打印的节点就是右子树的最左节点
假如当前节点的右子树为空,因为中序遍历是左,根,右的顺序进行遍历的,所以意味着这棵树已经结束,需要向上找孩子为父亲左孩子的祖先节点,其实就是找对应的根
当遍历完最后一个节点时,迭代器会一直向上沿着路径找孩子为父亲左孩子的祖先节点,找不到,
最后为空,则可以直接跳出循环
//实现前置++,保证指针往后移动,能够实现树的中序遍历
self& operator++()
{
//如果当前节点的右子树不为空
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
//如果当前节点的右子树为空,由于按照左子树,根,右子树遍历,说明此时该子树已经全部遍历
//需要向上找孩子为父亲左孩子的祖先节点
else
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
3.3.2 前置--
前置--则是相反操作,我们可以将它看作是按照右子树,根,左子树的顺序遍历整棵树,因此,进
行的操作和前置++其实完全是一样的,不过此时的left变成right,right变成left
//实现前置--
self& operator--()
{
//如果当前节点的左子树不为空
if (_node->_left)
{
// 1、左不为空,下一个就是左子树的最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
// 2、左为空,孩子是父亲的右的那个祖先
else
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
3.3.3 源码对比
源码中由于已经在基类已经实现过increment,decrement函数,因此在迭代器类已经继承,直接调
用即可,代码的整体思想是一样的,不过会有所差别
原因在于stl库中采用的红黑树结构会有自带一个头节点,为了进一步方便后面关联式容器set,
map的实现,为了与根节点进行区分,将头结点给成红色,并且让头结点的 pParent 域指向红黑树
的根节点,pLeft 域指向红黑树中最小的节点(leftmost),_pRight域指向红黑树中最大的节点
(rightmost)
void init() {
header = get_node();
color(header) = __rb_tree_red; // used to distinguish header from
// root, in iterator.operator++
root() = 0;
leftmost() = header;
rightmost() = header;
}
可以看到,由于多了一个header的头节点,因此increment函数需要作出相应的改变
void increment()
{
//右子树不为空,找右子树的最左节点
if (node->right != 0) {
node = node->right;
while (node->left != 0)
node = node->left;
}
//右子树为空,沿路径找节点为父节点的左孩子的祖先节点
else {
base_ptr y = node->parent;
while (node == y->right) {
node = y;
y = y->parent;
}
if (node->right != y)
node = y;
}
}
右子树不为空的情况都是一样的,但右子树为空的情况,因为header而变得复杂起来
右子树为空,最后迭代器指向的位置,肯定停留在父节点上
比如说5遍历完,迭代器++后,移动到的位置就是parent的位置,即源码中的y
但是为什么赋值前还需要判断节点的右孩子不与父节点相同呢?
因为会出现如图下面的情况,此时迭代器指向根节点,而恰好根节点没有右孩子,如果对迭代器进
行++操作,新的迭代器指向的就是header,而不需要再把y赋值给_node,这样++就能够继续推进
同理,由于多了一个header的头节点,在进行我们原来的所有操作前,还需要判断现在迭代器指
向的是否是头节点,假如是头节点,则进行迭代器--操作,则迭代器指向根节点
如果不进行该特殊情况判断,则进行--操作,反而迭代器会指向begin()
void decrement()
{
if (node->color == __rb_tree_red &&
node->parent->parent == node)
node = node->right;
else if (node->left != 0) {
base_ptr y = node->left;
while (y->right != 0)
y = y->right;
node = y;
}
else {
base_ptr y = node->parent;
while (node == y->left) {
node = y;
y = y->parent;
}
node = y;
}
}
下面给出stl库中源码,顺便包括了后置++,后置--的实现
//iterator基类
struct __rb_tree_base_iterator
{
typedef __rb_tree_node_base::base_ptr base_ptr;
typedef bidirectional_iterator_tag iterator_category;
typedef ptrdiff_t difference_type;
base_ptr node;
void increment()
{
//右子树不为空,找右子树的最左节点
if (node->right != 0) {
node = node->right;
while (node->left != 0)
node = node->left;
}
//右子树为空,沿路径找节点为父节点的左孩子的祖先节点
else {
base_ptr y = node->parent;
while (node == y->right) {
node = y;
y = y->parent;
}
if (node->right != y)
node = y;
}
}
void decrement()
{
if (node->color == __rb_tree_red &&
node->parent->parent == node)
node = node->right;
else if (node->left != 0) {
base_ptr y = node->left;
while (y->right != 0)
y = y->right;
node = y;
}
else {
base_ptr y = node->parent;
while (node == y->left) {
node = y;
y = y->parent;
}
node = y;
}
}
};
//iterator类运算符重载
self& operator++() { increment(); return *this; }
self operator++(int) {
self tmp = *this;
increment();
return tmp;
}
self& operator--() { decrement(); return *this; }
self operator--(int) {
self tmp = *this;
decrement();
return tmp;
}
3.3.4 其它运算符重载
源码中将这类运算符重载函数,全部改为了内联函数,其它没有什么变化
//自己实现的代码
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
//源码
inline bool operator==(const __rb_tree_base_iterator& x,
const __rb_tree_base_iterator& y) {
return x.node == y.node;
}
inline bool operator!=(const __rb_tree_base_iterator& x,
const __rb_tree_base_iterator& y) {
return x.node != y.node;
}
4.红黑树改造
4.1 红黑树的begin(),end()函数
由于在上面已经实现了迭代器的前置++,--运算符
接下来,只要在红黑树类实现begin,end函数,其实就可以用迭代器遍历这棵树了
对于我们模仿实现红黑树,由于不存在头节点,因此begin函数返回的就是红黑树的最左节点
end函数返回的是红黑树的最右节点的下一个节点,这里我们直接用空指针构造即可
同样实现const版本和普通迭代器两个版本
而由于源码中红黑树多了一个头节点,因此begin函数返回的是最左节点,而end函数返回的则是头
节点.
//我们自己模拟实现
iterator begin()
{
Node* cur = _root;
//找树的最左节点
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
const_iterator begin() const
{
Node* cur = _root;
//找树的最左节点
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
//源码
link_type& root() const { return (link_type&) header->parent; }
link_type& leftmost() const { return (link_type&) header->left; }
link_type& rightmost() const { return (link_type&) header->right; }
iterator begin() { return leftmost(); }
const_iterator begin() const { return leftmost(); }
iterator end() { return header; }
const_iterator end() const { return header; }
4.2 红黑树的模板参数
与我们之前模拟实现的红黑树不同,除了Key,Value两个参数外,源码中的红黑树有五个参数
除去最后一个和空间支配器相关的参数外,还增加了两个KeyOfValue,和Compare参数
第一个Key参数发挥的作用我们比较好理解,因为无论是map,或者是set,底层封装的这棵红黑
树,它首先是一棵搜索平衡二叉树,是按照key来建树的,find,erase这些函数接口参数都是key,
如果单纯只有一个Value模板,显然是不够的
第二个Value参数主要是服务于insert函数,它决定了树的节点里面存的是什么,map存的就是
pair,set存的就是key
第三个KeyOfValue参数,则是服务于比较
RBtree判断不了你是map还是set,我们需要自己在封装map和set的时候提供对应的Value,以此
完成红黑树类中值的判断
- 比如set的keyofvalue就是它自己的key
- 而map的keyofvalue是
pair.first
//对于set类型而言,其value比较的是key,所以增加了一个模板参数
//改造前
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first > key)
{
cur = cur->_left;
}
else if (cur->_kv.first < key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
//改造后
//假如是set,则传入的则是key比较;假如是map。传入的则是pair.first
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfValue kot;
while (cur)
{
if (kot(cur-> _data) > key)
{
cur = cur->_left;
}
else if (kot(cur-> _data) < key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
第四个Compare参数则是给用户机会,能够提供比较的方法,键值大于往左边插入还是往右边插
入等等
4.3 下标访问
map非常特殊的一点就是,可以直接用map[key]=value
来修改value的内容,如果不存在,则直接
插入相应的新节点
原理也很简单,就是稍微改造一下insert函数,不单单返回bool,来判断是否插入成功,还需要返
回插入元素的迭代器,通过迭代器,我们就可以在map中重载方括号运算符,来修改对应的Value
//修改后的insert函数代码
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;
KeyOfValue kot;
while (cur)
{
if (kot(cur-> _data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur-> _data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return make_pair(iterator(cur),false);
}
}
//建新节点
cur = new Node(data);
Node* newnode = cur;
if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//调整节点颜色
while (parent && parent->_col == RED)
{
//找爷爷
Node* grandfather = parent->_parent;
//父亲为爷爷的左节点
if (grandfather->_left == parent)
{
//则叔叔是爷爷的右节点
Node* uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
//父亲和叔叔节点都调节为黑色
parent->_col = BLACK;
uncle->_col = BLACK;
//爷爷调节为红色
grandfather->_col = RED;
//往上调节
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// p u
// c
//右单旋
if (cur == parent->_left)
{
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// p u
// c
//LR双旋
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
//父亲为爷爷的右节点
else
{
//则叔叔是爷爷的左节点
Node* uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
//父亲和叔叔节点都调节为黑色
parent->_col = BLACK;
uncle->_col = BLACK;
//爷爷调节为红色
grandfather->_col = RED;
//往上调节
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
//左单旋
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
//RL双旋
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode),true);
}
源码的insert_unique的实现
// insert/erase
//插入声明
pair<iterator,bool> insert_unique(const value_type& x);
//源码中插入的具体实现
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_unique(const Value& v)
{
link_type y = header;
//从根节点开始往下找合适的插入点
link_type x = root();
bool comp = true;
while (x != 0) {
y = x;
//小于则true,大于等于则false,循环中只会出现一次false
comp = key_compare(KeyOfValue()(v), key(x));
//x遇上大,则往左走;遇到小于等于,则往右走
x = comp ? left(x) : right(x);
}
//x为新插入的节点位置,y为其父节点,v为插入节点的value值
//循环结束时,此时x必为空,y必为叶节点
//令迭代器j指向父节点y
iterator j = iterator(y);
//如果comp为真,说明x位于y的左边
if (comp)
//如果j指向树的最左节点,则直接插入
if (j == begin())
return pair<iterator,bool>(__insert(x, y, v), true);
else
//插入点之父节点不为最左节点,回调j
--j;
//小于新值,则往右进行插入
if (key_compare(key(j.node), KeyOfValue()(v)))
return pair<iterator,bool>(__insert(x, y, v), true);
//到这一步说明键值重复,那么就不该插入该值,返回false
return pair<iterator,bool>(j, false);
}
PS:上述代码展示的是插入中不允许重复元素
源码解决重复问题的方法也比较巧妙
父节点为最左节点,也就是最小的,插入的新节点比父节点还小,则插入的新节点一定不是重复元素,可以直接插入
如果不是最左节点,就会出现两种情况
第一种情况,comp为真,此时要回调父节点,--j,再判断是否会有重复元素
比如说【8 9 11】,往里面插入10,就需要判断9,如果9小于10,则插入
第二种,comp为假
比如说[【9 8 13 10 12】,往里面插入11,11大于10,往右直接插入
关键其实在于理解,假如出现相同元素后,会发生什么?
x会往右走,然后一直往左移动,因为右子树所有的元素一定比根要大
五.完整代码展示
#include <iostream>
using namespace std;
enum Colour {
RED = 0,
BLACK,
};
//为了实现map与set封装使用同一个模板红黑树,前者的value是pair,后者的value为key
//因此我们需要对红黑树的模板进行改造
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)
, _col(RED)
{}
};
//树的迭代器构造
template<class T, class Ref, class Ptr>
struct __RBtree_iterator
{
typedef RBTreeNode<T> Node;
typedef __RBtree_iterator<T, Ref, Ptr> self;
Node* _node;
__RBtree_iterator() {}
__RBtree_iterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator<T, T&, T*> iterator; 拷贝构造
// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
// 支持普通迭代器构造const迭代器的构造函数
__RBtree_iterator(const __RBtree_iterator<T, T&, T*>& it)
:_node(it._node)
{}
//实现前置++,保证指针往后移动,能够实现树的中序遍历
self& operator++()
{
//如果当前节点的右子树不为空
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
//如果当前节点的右子树为空,由于按照左子树,根,右子树遍历,说明此时该子树已经全部遍历
//需要向上找孩子为父亲左孩子的祖先节点
else
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
//实现前置--
self& operator--()
{
//如果当前节点的左子树不为空
if (_node->_left)
{
// 1、左不为空,下一个就是左子树的最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
// 2、左为空,孩子是父亲的右的那个祖先
else
{
Node* parent = _node->_parent;
Node* cur = _node;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
};
//仿函数
template<class K, class T, class KeyOfValue>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
public:
typedef __RBtree_iterator<T, T&, T*> iterator;
typedef __RBtree_iterator<T, const T&, const T*> const_iterator;
iterator begin()
{
Node* cur = _root;
//找树的最左节点
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
const_iterator begin() const
{
Node* cur = _root;
//找树的最左节点
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
//对于set类型而言,其value比较的是key,所以增加了一个模板参数
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfValue kot;
while (cur)
{
if (kot(cur-> _data) > key)
{
cur = cur->_left;
}
else if (kot(cur-> _data) < key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
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;
KeyOfValue kot;
while (cur)
{
if (kot(cur-> _data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur-> _data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return make_pair(iterator(cur),false);
}
}
//建新节点
cur = new Node(data);
Node* newnode = cur;
if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//调整节点颜色
while (parent && parent->_col == RED)
{
//找爷爷
Node* grandfather = parent->_parent;
//父亲为爷爷的左节点
if (grandfather->_left == parent)
{
//则叔叔是爷爷的右节点
Node* uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
//父亲和叔叔节点都调节为黑色
parent->_col = BLACK;
uncle->_col = BLACK;
//爷爷调节为红色
grandfather->_col = RED;
//往上调节
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// p u
// c
//右单旋
if (cur == parent->_left)
{
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// p u
// c
//LR双旋
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
//父亲为爷爷的右节点
else
{
//则叔叔是爷爷的左节点
Node* uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
//父亲和叔叔节点都调节为黑色
parent->_col = BLACK;
uncle->_col = BLACK;
//爷爷调节为红色
grandfather->_col = RED;
//往上调节
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
//左单旋
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
//RL双旋
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(newnode),true);
}
int Height()
{
return _Height(_root);
}
bool IsRBTree()
{
//假如根节点存在,但颜色不是黑色,则不是红黑树
if (_root && _root->_col == RED)
{
cout << "根节点颜色是红色" << endl;
return false;
}
//随便选一条路径作为黑色节点参考点
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
benchmark++;
cur = cur->_left;
}
// 连续红色节点
return _Check(_root, 0, benchmark);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
bool _Check(Node* root, int blackNum, int benchmark)
{
//假如到空节点(叶子节点),说明已经走完一条路径,可以开始判断
if (root == nullptr)
{
//假如统计出的黑色节点个数和参考黑色节点个数不同,则一定不是红黑树
if (blackNum != benchmark)
{
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
//递归遇到黑色节点时,则blackNum可以加1
if (root->_col == BLACK)
blackNum++;
//假如连续存在两个红色节点,则也不是红黑树,注意还需要判断父节点是否存在
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << "存在连续的红色节点" << endl;
return false;
}
//递归判断是否是红黑树,左子树和右子树都为红黑树,则为红黑树
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
//左旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
//b变成30的右
parent->_right = subRL;
//父节点也需要调整,但subRL可能为空
if (subRL)
subRL->_parent = parent;
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
//subR重新链接回爷爷的左或者右
if (ppNode->_right == parent)
{
ppNode->_right = subR;
}
else
{
ppNode->_left = subR;
}
subR->_parent = ppNode;
}
}
//右旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//b变成60的左
parent->_left = subLR;
//父节点也需要调整,但subRL可能为空
if (subLR)
subLR->_parent = parent;
//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
//subL重新链接回爷爷的左或者右
if (ppNode->_right == parent)
{
ppNode->_right = subL;
}
else
{
ppNode->_left = subL;
}
subL->_parent = ppNode;
}
}
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_kv.first << " ";
_Inorder(root->_right);
}
private:
Node* _root = nullptr;
};