写一个自己想要的“set”,想实现什么功能直接在里面添加。STL虽然把set封装的很好,很强大,易于扩展,但是正由于兼容性很好,封装太多层,牺牲了很多性能。废话不多说,先晒出我的测试结果:
测试用的是谷歌codejam的一道题,提供了一百个测试用例,这道题需要频繁调用set的插入与删除,用来测试再好不过。
题目地址:https://code.google.com/codejam/contest/6364486/dashboard(需要小工具哦)
下面是我下载的一个AC代码
#include
#include
#include
using namespace std;
ifstream infile;
ofstream outfile;
const int MAXN = 500005;
int X[MAXN];
int S[MAXN];
long long sum[MAXN];
void erase_one(multiset &s, long long x) {
auto it = s.find(x);
auto it1 = it;
it1++;
s.erase(it++);
}
void solve() {
int N, O;
long long D;
infile >> N >> O >> D;
int X1, X2, A, B, C, M, L;
infile >> X1 >> X2 >> A >> B >> C >> M >> L;
X[1] = X1, X[2] = X2;
for (int i = 3; i <= N; i++) {
X[i] = (1LL * A * X[i - 1] + 1LL * B * X[i - 2] + C) % M;
}
for (int i = 1; i <= N; i++) {
S[i] = X[i] + L;
sum[i] = sum[i - 1] + S[i];
}
int cnt_odd = 0;
int l = 1;
multiset s;
long long ans = -0x3f3f3f3f3f3f3f3fLL;
for (int i = 1; i <= N; i++) {
s.insert(sum[i - 1]);
cnt_odd += S[i] & 1;
while (cnt_odd > O) {
erase_one(s, sum[l - 1]);
//s.erase_unique(sum[l - 1]);
cnt_odd -= S[l] & 1;
l++;
}
auto it = s.lower_bound(sum[i] - D);
if (it != s.end()) {
ans = std::max(ans, sum[i] - *it);
}
}
if (ans == -0x3f3f3f3f3f3f3f3fLL) {
outfile << "IMPOSSIBLE" << endl;
}
else {
outfile << ans << endl;
}
}
int main() {
infile.open("F:\\谷歌下载\\A-large-practice.in");
outfile.open("F:\\谷歌下载\\A-large-practice.out");
int T;
infile >> T;
for (int i = 1; i <= T; i++) {
cout << i << endl;
outfile << "Case #" << i << ": ";
solve();
}
return 0;
}
for循环中频繁插入删除,大部分测试用例N都是等于50 万,我们来看看用时
14分钟,如果你在做谷歌笔试题,这个时间可能会让你抓狂。
再看看用了自己写的set后
你没看错,仅需要一分钟19秒。快了接近十倍。
再看看结果对不对
显示correct是对的。
是我的算法效率更高?我看了源码,并不是,大牛们在设计STL考虑到兼容性,可扩展性,不可避免的损失了一部分性能。
废话不多说上代码:
先给红黑树搞个数据结构,就是五大点,左右孩子,父母,key,颜色color;两个函数分别是寻找前驱,寻找后继,因为在后面的迭代器与类中都要用到,为了避免代码重复,放在了结构体中。(全局函数也行)
template
struct RB_TreeNode
{
RB_TreeNode * left;
RB_TreeNode * right;
RB_TreeNode * p;
T key;
color color;
RB_TreeNode (T x,RB_TreeNode* _Guard) :key(x), left(_Guard), right(_Guard), p(_Guard), color(red) {};
RB_TreeNode () : key(T()),left(NULL), right(NULL), p(NULL), color(black) {};
RB_TreeNode(T val) :key(val),left(NULL), right(NULL), p(NULL), color(black) {};
void operator=(RB_TreeNode& oth) {
left = oth.left;
color = oth.color;
right = oth.right;
key = oth.key;
p = oth.p;
}
//寻找前驱
RB_TreeNode* findFront(RB_TreeNode* _Guard)
{
RB_TreeNode* t = this;
if (t == _Guard) return t;
if (t->left != _Guard)
{
t = t->left;
while (t->right != _Guard)
t = t->right;
return t;
}
else
{
while (t->p != _Guard&&t == t->p->left)
t = t->p;
return t->p;
}
}
//寻找后继
RB_TreeNode* findSucceed(RB_TreeNode* _Guard)
{
RB_TreeNode* t = this;
if (t == _Guard) return t;
if (t->right != _Guard)
{
t = t->right;
while (t->left != _Guard)
t = t->left;
return t;
}
else
{
while (t->p != _Guard&&t == t->p->right)
t = t->p;
return t->p;
}
}
};
那既然是红黑树,不可避免要说到它的五个特性:
1 每个节点非黑即红;
2 根节点黑色
3 每个叶节点黑色
4 如果节点为红色,则俩孩子都为黑色
5 对于每个节点,该节点到叶节点的每条路径黑高度都相同
我们来看看这种数据结构有什么好处,在此之前,先证明一个公式,以x为根的节点内节点个数至少是2的bh次方减一个,对于高度为0的节点显然成立,为0;每个内节点都有两个子女,若X的黑高度为bh,则儿子的黑高度至少为bh-1,如果这个公式对于子女成立,则对于跟至少有2^(bh-1)-1+2^(bh-1)-1=2^bh-1个节点,归纳假设成立。若树内节点总数为n,则有n>=2^bh-1;而由性质1,4可知,黑节点个数大于等于红色节点个数的,故总高度h<=bh*2;因此n>=2^(h/2)-1;得到高度h<=2lg(n+1);这意味着搜索时间将是对数时间。
当然了,红黑树的优势不止于此,不然直接使用AVL树好了,搜索时间更佳,红黑树另一个特性是删除后所执行的旋转操作量级是O(1),而AVL树是O(lgn),而且红黑树对插入删除节点没有AVL树那么敏感。
至于红黑树的插入删除先留在后面讲插入删除代码的时候讲。
下面一部分一部分介绍代码,总代码放在最后
template
struct rbtree_iterator :public iterator {
typedef rbtree_iterator iterator;
typedef RB_TreeNode* link_type;
typedef rbtree_iterator self;
link_type node;
link_type _Guard;
rbtree_iterator() {};
rbtree_iterator(const link_type& x,const link_type& g):node(x),_Guard(g) {};
rbtree_iterator(const iterator& it) :node(it.node), _Guard(it._Guard) {};
self& operator=(const self& oth) { node = oth.node; return *this; };
ref operator*() const { return node->key; };
ptr operator->() const { return &(node->key); };
self& operator++() { node = node->findSucceed(_Guard); return *this; };
self operator++(int) { self tmp = *this; node = node->findSucceed(_Guard); return tmp; };
self& operator--() { node = node->findFront(_Guard); return *this; };
self operator--(int) { self tmp = *this; node = node->findFront(_Guard); return tmp; };
bool operator==(const self& oth) { return node == oth.node; };
bool operator!=(const self& oth) { return !(node == oth.node); };
};
为了与STL契合,必须为RBTree设计一个专属迭代器的类,在这里我继承了iterator类的iterator
bidirectional_iterator_tag 是可向前可向后的迭代器,迭代器的类型划分,具体的可以看STL源码剖析,里面很详细。
在迭代器这个类中,有一个link_type类型的节点,在这里他的类型就是我们树的节点类型,RB_TreeNode
在类的开始,我写了一个仿函数,作为RBTree比较函数的默认参数,如果对象重载了小于号,则无需传入比较函数,注意到我们将给迭代器类模板参数进行了限定,并进行了重命名,参数限定,是为了与我们的类匹配,重命名,Emmmm,STL迭代器都叫这名字。
然后就是构造函数与操作符重载
RBTree(const keycompare& comp = keycompare());//默认构造函数
RBTree(const RBTree& oth);
RBTree(RBTree&& oth);//移动构造函数
template
RBTree(_itr begin, _itr end);//如果传入的是迭代器
virtual~RBTree();
///操作符重载
RBTree& operator=(const RBTree& oth);
RBTree& operator=(RBTree&& oth);
const_iterator operator[](int i);
这里是一些功能函数(基本想要实现的都有)
void clear();//清空树
int size();//返回树的数量
bool empty();//判空
int count(T val);//与val相等的元素的数量
void insert(T value);//插入元素,可重复
void insert_unique(T value);//插入元素,如果重复,后来的会代替现在的。
//想怎么删除,就怎么删除
void erase_unique(T value);//删除一个等于value的元素
void erase_equal(T value); //删除所有等于value的元素
void erase(const_iterator& l, const_iterator& r);//删除迭代器区间的元素
void erase(const_iterator& start, int n);//删除从指定迭代器开始的n个元素
void erase(const_iterator& it);//删除迭代器指向的元素
////你要的迭代器 我都有
const_iterator begin();//返回最小元素的迭代器
const_iterator end(); //返回一个边界,跟STL其他容器的end()差不多
const_iterator back(); //返回最大元素的迭代器
const_iterator start(); //返回一个边界,只不过是前边界,实际与end()地址相同,为了区别写了这么个函数
const_iterator find(T val);//寻找指定元素第一次出现,返回一个迭代器;
const_iterator findlast(T val);//寻找指定元素最后一次出现,返回一个迭代器;
const_iterator lower_bound(T val);//如果元素存在,返回它第一次出现时的迭代器,若不存在返回它下边界迭代器
const_iterator upper_bound(T val);//元素存在,返回最后一次出现迭代器,不存在,返回上边界迭代器
这些函数都被我一一测试过都可用,至于具体实现,看最后代码。
为了实现上面的功能,写了以下辅助函数
//测试用的函数
void pre_traverse();
void mid_traverse(T* pt = NULL);
void post_traverse();
void print();
RB_TreeNode* guard();
RB_TreeNode * getroot();
private:
void RBerase(RB_TreeNode* node);//删除函数
RB_TreeNode* RBTree_serch(T value, RB_TreeNode* beginRoot);//寻找函数
void RBTree_insert(T value, bool isunique);//插入函数
void Left_Rotate(RB_TreeNode * t);//左旋
void Right_Rotate(RB_TreeNode * t);//右旋
void RBDELETE_Fixup(RB_TreeNode * t);//删除后调整红黑特性
void RBINSERT_Fixup(RB_TreeNode * t);//插入后调整红黑特性
RB_TreeNode* findFront(RB_TreeNode* t);//寻找前驱
RB_TreeNode* findSucceed(RB_TreeNode * t);//寻找后继
const_iterator findFandB(RB_TreeNode* target, bool isForward);//寻找与指定值相等的最前面元素与最后面元素
RB_TreeNode * copyTree(const RB_TreeNode* oth, RB_TreeNode* p, RB_TreeNode*const& Guard);//拷贝
void distroy(RB_TreeNode* root);//销毁
int findindex(RB_TreeNode* root, int num, RB_TreeNode*& tar, int& index);
void relationGuard();
void printNode(RB_TreeNode * outT);
RB_TreeNode* _guard;
RB_TreeNode* _root;
int _len;
keycompare com;
friend ostream& operator<<(ostream &out, RB_TreeNode * outT);
下面讲一讲红黑树的插入:
具体代码:
template>
void RBTree::RBTree_insert(T value, bool isunique) {
RB_TreeNode * Tnode = new RB_TreeNode(value, _guard);
RB_TreeNode * tmp = _root;
RB_TreeNode * Ptmp = _guard;
if (_root == _guard)//如果插入的节点是第一个节点,相当于初始化根节点
{
_root = Tnode;
_root->color = black;
_len++;
_guard->left = _root;
_guard->right = _root;
return;
}
//如果插入的节点小于最小值,直接插在最左边,并更新最小值节点
if (com(value, _guard->left->key)) {
Tnode->p = _guard->left;
_guard->left->left = Tnode;
_guard->left = Tnode;
}
//如果插入的节点大于等于最大值,直接插在最右边,更新最大值节点
else if (!com(value, _guard->right->key)) {
if (!com(_guard->right->key, value) && isunique) {
swap(_guard->right->key, tmp->key);
delete Tnode;
return;
}
Tnode->p = _guard->right;
_guard->right->right = Tnode;
_guard->right = Tnode;
}
else {
///以上三种情况都不属于寻找合适的插入节点,就一直往下找,直到边界条件(找到了空节点,在我这是_guard)
while (tmp != _guard)
{
Ptmp = tmp;
if (com(value, tmp->key))
tmp = tmp->left;
else if (com(tmp->key, value))
tmp = tmp->right;
else {
if (isunique) {
swap(Tnode->key, tmp->key);
delete Tnode;
return;
}
tmp = tmp->right;
}
}
///更新插入节点的父亲,以及该父亲的孩子须得指向它
Tnode->p = Ptmp;
if (com(Tnode->key, Ptmp->key))
Ptmp->left = Tnode;
else
Ptmp->right = Tnode;
}
///如果插入节点的父亲是红色,则违反了红黑树红黑特性,需要调整
if (Tnode->p->color == red)
RBINSERT_Fixup(Tnode);
_len++;///别忘了更新元素数目
}
需要注意的是,一般树的边界都是NULL,在这里我有个特殊节点_guard,是所有叶节点的孩子,也就是说,在这里他就是边界,而且它也是 根_root的父母,其左右孩子分别指向最小值跟最大值,所以在插入的时候要注意更新
插入节点的父亲如果是红色,由于插入节点的颜色默认为红色,此时便违反了红黑树性质4中的如果节点颜色为红,则孩子必须为黑。这种情况我们需要调整红黑树,也就是我函数中的
void RBDELETE_Fixup(RB_TreeNode * t);//删除后调整红黑特性
void RBINSERT_Fixup(RB_TreeNode * t);//插入后调整红黑特性
在讲这个之前,先讲一讲左旋与右旋,我转载了一个有趣而生动的图(博客地址,图上有):
//////左旋
template>
void RBTree::Left_Rotate(RB_TreeNode * t)
{
if (t == _guard || t->right == _guard)
{
cout << "左旋节点或节点右孩子不应为空" << endl;
return;
}
RB_TreeNode * tmp = t->right;
tmp->p = t->p;
if (t->p == _guard)
{
_root = tmp;
}
else if (t == t->p->left)
t->p->left = tmp;
else
t->p->right = tmp;
t->p = tmp;
t->right = tmp->left;
if (tmp->left != _guard)
tmp->left->p = t;
tmp->left = t;
}
////右旋
template>
void RBTree::Right_Rotate(RB_TreeNode * t)
{
if (!t || !t->left)
{
cout << "右旋节点或节点左孩子不应为空" << endl;
return;
}
RB_TreeNode * tmp = t->left;
tmp->p = t->p;
if (t->p == _guard)
{
_root = tmp;
}
else if (t == t->p->left)
t->p->left = tmp;
else
t->p->right = tmp;
t->p = tmp;
t->left = tmp->right;
if (tmp->right != _guard)
tmp->right->p = t;
tmp->right = t;
}
调整红黑树的颜色,主要分三种情况(当插入节点父亲为红色的时候)
下面所有图均转载自:https://blog.csdn.net/lucienduan/article/details/38880523
第一种情况:如图叔叔是红色,此时将父亲跟叔叔变成黑色,祖父变成红色,这时我们令node等于祖父,如果祖父的父亲还是红色,继续进入循环,如果祖父父亲是黑色,则退出。也就是刚才node面临的情况现在到了祖父头上
第二种情况与第三种情况放一起讲,我们把第二种情况进行一个左旋,变成第三种情况,叔叔是黑色。这种情况将祖父节点来一个右旋,并把祖父节点变成红色,父节点变成黑色,这样黑高度保持不变,也满足了性质4
总结一下。红黑树插入,如果插入节点父亲是红色,则需要调整红黑树。此时可分为三种情况,其实是两种情况,当叔叔是红色和当叔叔是黑色,当叔叔是红色,直接把叔叔跟父亲染黑,祖父染红(祖父原来肯定是黑色,不然在插入之前,这颗红黑树就违反了性质4),然后祖父节点代替原来的节点进入检查,一直向上升,直到遇到叔叔是黑色或者上升到根节点。
当叔叔是黑色,此时分为插入节点本身是是左孩子还是右孩子,如果是右孩子,一波左旋,变成情况3,当然了此时的父亲孩子角色换了,不过这部重要,重要的是形状。变成情况三一波右旋,把父亲节点顶上去,染成黑色,祖父变成红色。
这里只讨论了插入节点是左孩子的情况,如果是右孩子,对称
下面是代码:
template>
void RBTree::RBINSERT_Fixup(RB_TreeNode * t)
{
RB_TreeNode * tmp = t->p;
RB_TreeNode * Pbra = _guard;
//出递归的条件,就是节点是黑色
while (tmp->color==red)
{
//这个就是我们讨论的当插入节点是左孩子的时候
if (tmp == tmp->p->left)
{
Pbra = tmp->p->right;
///第一种情况 叔叔是红色
if (Pbra!=_guard&&Pbra->color == red)
{
tmp->color = black;
tmp->p->color = red;
Pbra->color = black;
t = tmp->p;//当前节点变成祖父节点
tmp = t->p;//同时更新父节点为祖父节点的父亲节点
}
else
{
///叔叔是黑色
if (t == tmp->right)///插入节点是右孩子,左旋,变成第三种情况
{
Left_Rotate(tmp);
tmp = t;//注意这里左旋之后,孩子父亲角色换了
}
tmp->color = black;//tmp颜色变黑,达到出循环的条件
tmp->p->color = red;
Right_Rotate(tmp->p);
}
}
else//这边是插入节点是右孩子的情况,与上面是对称的
{
Pbra = tmp->p->left;
if (Pbra!=_guard&&Pbra->color == red)
{
tmp->color = black;
tmp->p->color = red;
Pbra->color = black;
t = tmp->p;
tmp = t->p;
}
else
{
if (t == tmp->left)
{
Right_Rotate(tmp);
tmp = t;
}
tmp->color = black;
tmp->p->color = red;
Left_Rotate(tmp->p);
}
}
if (tmp==_guard)//说明当前节点是根节点,因为只有根节点的父节点是边界节点_guard
{
///这里其实可以直接将t的颜色变成黑色,然后直接出循环
tmp = t;
tmp->color = black;
}
}
}
下面是删除代码,如果节点有一个孩子是边界节点,那么直接把节点删除,节点的父亲与节点的另一个孩子相连。如果节点的左右孩子都不是边界节点,那么找到它的后继节点,然后交换两个节点的信息,删除后后继节点。其实在交换过程中,最简单的就是交换两者的key,这样节点的结构就没变化,不用更新左右孩子与父亲(这个更新起来巨麻烦六个指针),但是如果这样,指向后继的迭代器就会失效。所以必须交换两块内存。
下面是代码:
template>
void RBTree::RBTree_insert(T value, bool isunique) {
RB_TreeNode * Tnode = new RB_TreeNode(value, _guard);
RB_TreeNode * tmp = _root;
RB_TreeNode * Ptmp = _guard;
if (_root == _guard)//如果插入的节点是第一个节点,相当于初始化根节点
{
_root = Tnode;
_root->color = black;
_len++;
_guard->left = _root;
_guard->right = _root;
return;
}
//如果插入的节点小于最小值,直接插在最左边,并更新最小值节点
if (com(value, _guard->left->key)) {
Tnode->p = _guard->left;
_guard->left->left = Tnode;
_guard->left = Tnode;
}
//如果插入的节点大于等于最大值,直接插在最右边,更新最大值节点
else if (!com(value, _guard->right->key)) {
if (!com(_guard->right->key, value) && isunique) {
swap(_guard->right->key, tmp->key);
delete Tnode;
return;
}
Tnode->p = _guard->right;
_guard->right->right = Tnode;
_guard->right = Tnode;
}
else {
///以上三种情况都不属于寻找合适的插入节点,就一直往下找,直到边界条件(找到了空节点,在我这是_guard)
while (tmp != _guard)
{
Ptmp = tmp;
if (com(value, tmp->key))
tmp = tmp->left;
else if (com(tmp->key, value))
tmp = tmp->right;
else {
if (isunique) {
swap(Tnode->key, tmp->key);
delete Tnode;
return;
}
tmp = tmp->right;
}
}
///更新插入节点的父亲,以及该父亲的孩子须得指向它
Tnode->p = Ptmp;
if (com(Tnode->key, Ptmp->key))
Ptmp->left = Tnode;
else
Ptmp->right = Tnode;
}
///如果插入节点的父亲是红色,则违反了红黑树红黑特性,需要调整
if (Tnode->p->color == red)
RBINSERT_Fixup(Tnode);
_len++;///别忘了更新元素数目
}
/////删除节点
template>
void RBTree::RBerase(RB_TreeNode* node)
{
RB_TreeNode* t = node;
if (t == _guard) return;
RB_TreeNode* tmp;
if (t->left != _guard&&t->right != _guard)
{
///节点左右孩子都不是边界节点,交换信息
RB_TreeNode* succ = findSucceed(t);
if (t == t->p->left)
t->p->left = succ;
else
t->p->right = succ;
if (succ == succ->p->left)
succ->p->left = t;
else
succ->p->right = t;
swap(succ->p, t->p);
swap(succ->color, t->color);
swap(succ->left, t->left);
swap(succ->right, t->right);
succ->left->p = succ;
succ->right->p = succ;
if (t->left != _guard)
t->left->p = t;
if (t->right != _guard)
t->right->p = t;
if (succ->p == _guard) _root = succ;
//这里其实最简单的方法是交换两个key然后直接删除succ,但是这样做会令原来指向succ的迭代器失效,所以要逐个交换。
}
///这里就是的tmp就是我们需要跟删除节点父亲建立联系的孩子节点
if (t->left == _guard)
tmp = t->right;
else
tmp = t->left;
///如果删除的是最小值或者最大值,要更新_guard孩子
if (t == _guard->left)
_guard->left = findSucceed(t);
if (t == _guard->right)
_guard->right = findFront(t);
if (t->p == _guard) _root = tmp;///如果是根,要及时更新根节点
else if (t == t->p->left)///tmp 取代原来t的位置
t->p->left = tmp;
else
t->p->right = tmp;
tmp->p = t->p;
if (t->color == black)///如果被删除的节点是黑色,需要调整红黑树红黑特性
{
RBDELETE_Fixup(tmp);
}
_len--;
delete t;
t = NULL;
return;
}
如果被删除节点是黑色,那么这条路径黑高度下降,需要调整颜色,删除情况比插入复杂,我们可以假想成被删除节点的黑色被加在了孩子节点上,这样孩子节点就是两层颜色,多了一层黑色,大概可分为四种情况:
以下所有图均转载自:https://blog.csdn.net/lucienduan/article/details/38880523
第一种情况,兄弟是红色,直接右旋,并将父亲节点染红。这样节点的兄弟就变成黑色了,变成第2,3,4种情况
第二种情况:兄弟是黑,且兄弟的左右孩子都是黑色,这种情况吧兄弟染红,上推至父亲节点,node节点本来多的那一层黑色被移到父亲节点身上了,现在父亲节点身上有两层黑色,继续进入循环
第三种情况是兄弟黑,且兄弟的左孩子红色,右孩子黑色,此时我们将兄弟节点跟左孩子节点颜色交换,并将兄弟右旋,此时变成了情况4,当然别忘了,此时的兄弟与孩子的父亲儿子身份交换了,现在的兄弟是与原来兄弟的右孩子,此时兄弟右孩子是红色
第四种情况兄弟右孩子为红色,将父亲节点左旋,并把将兄弟的黑色传给右孩子,将父亲节点的颜色传给兄弟,然后将node的额外黑色给父亲。这样NODE去掉了额外的黑色,且保持了黑高度。
代码如下:
///删除节点后调整红黑树满足红黑树五个特性
template>
void RBTree::RBDELETE_Fixup(RB_TreeNode * t)
{
RB_TreeNode * w = NULL;
while (t!=_root&&t->color==black)
{
////要连接的孩子节点是左孩子的情况
if (t == t->p->left)
{
w = t->p->right;//兄弟节点
////第一种情况
if (w->color == red)///兄弟是红色
{
///交换兄弟与父亲的颜色,因为兄弟颜色是红色,父亲肯定是黑色,所以直接赋值
t->p->color = red;
w->color = black;
Left_Rotate(t->p);
}
else
{
///兄弟右孩子是红色,就是第四种情况
if (w->right->color == red)
{
w->color = t->p->color;
t->p->color = black;
w->right->color = black;
Left_Rotate(t->p);
t = _root;///这里是为了最后一句,出循环后要把t染成黑色,将t设置为根节点,可以出循环,将根节点染黑没影响
}
else if (w->left->color == red)///右孩子是黑色,左孩子红色
{
w->color = red;
w->left->color = black;
Right_Rotate(w);
w = w->p;//别忘了右旋之后更新兄弟
}
else
{
///两个孩子都是黑色,也就是第二种情况,把兄弟染红,将另一重黑色往上推,也就是父亲节点
w->color = red;
t = t->p;
}
}
}
///这边是当前节点是右孩子的情况 完全对称
else
{
w = t->p->left;
if (w->color == red)
{
t->p->color = red;
w->color = black;
Right_Rotate(t->p);
}
else
{
if (w->left != _guard&&w->left->color == red)
{
w->color = t->p->color;
t->p->color = black;
w->left->color = black;
Right_Rotate(t->p);
t = _root;
}
else if (w->right != _guard&&w->right->color == red)
{
w->color = red;
w->right->color = black;
Left_Rotate(w);
w = w->p;
}
else
{
w->color = red;
t = t->p;
}
}
}
}
t->color = black;///出循环要么是t是红色,要么t是根节点,直接染黑。
}
下面是完整代码,有兴趣可以回去用用试试看(哈哈哈)
#pragma once
#include
#include
#include
#include
#include
#include
using namespace std;
enum color
{
red,
black
};
template
struct RB_TreeNode
{
RB_TreeNode * left;
RB_TreeNode * right;
RB_TreeNode * p;
T key;
color color;
RB_TreeNode (T x,RB_TreeNode* _Guard) :key(x), left(_Guard), right(_Guard), p(_Guard), color(red) {};
RB_TreeNode () : key(T()),left(NULL), right(NULL), p(NULL), color(black) {};
RB_TreeNode(T val) :key(val),left(NULL), right(NULL), p(NULL), color(black) {};
void operator=(RB_TreeNode& oth) {
left = oth.left;
color = oth.color;
right = oth.right;
key = oth.key;
p = oth.p;
}
//寻找前驱
RB_TreeNode* findFront(RB_TreeNode* _Guard)
{
RB_TreeNode* t = this;
if (t == _Guard) return t;
if (t->left != _Guard)
{
t = t->left;
while (t->right != _Guard)
t = t->right;
return t;
}
else
{
while (t->p != _Guard&&t == t->p->left)
t = t->p;
return t->p;
}
}
//寻找后继
RB_TreeNode* findSucceed(RB_TreeNode* _Guard)
{
RB_TreeNode* t = this;
if (t == _Guard) return t;
if (t->right != _Guard)
{
t = t->right;
while (t->left != _Guard)
t = t->left;
return t;
}
else
{
while (t->p != _Guard&&t == t->p->right)
t = t->p;
return t->p;
}
}
};
// :public iterator
template
struct rbtree_iterator {
typedef rbtree_iterator iterator;
typedef RB_TreeNode* link_type;
typedef rbtree_iterator self;
typedef bidirectional_iterator_tag iterator_category;
typedef value value_type;
typedef ptr pointer;
typedef ref reference;
typedef ptrdiff_t difference_type;
link_type node;
link_type _Guard;
rbtree_iterator() {};
rbtree_iterator(const link_type& x,const link_type& g):node(x),_Guard(g) {};
rbtree_iterator(const iterator& it) :node(it.node), _Guard(it._Guard) {};
self& operator=(const self& oth) { node = oth.node; return *this; };
ref operator*() const { if (node == _Guard) { cout << "尝试访问边界迭代器" << endl; throw; } return node->key; };
ptr operator->() const { if (node == _Guard) { cout << "尝试访问边界迭代器" << endl; throw; }return &(node->key); };
self& operator++() { if (node == _Guard) { cout << "迭代器向后越界" << endl; throw; } node = node->findSucceed(_Guard); return *this; };
self operator++(int) { if (node == _Guard) { cout << "迭代器向后越界" << endl; throw; } self tmp = *this; node = node->findSucceed(_Guard); return tmp; };
self& operator--() { if (node == _Guard) { cout << "迭代器向前越界" << endl; throw; } node = node->findFront(_Guard); return *this; };
self operator--(int) { if (node == _Guard) { cout << "迭代器向前越界" << endl; throw; } self tmp = *this; node = node->findFront(_Guard); return tmp; };
bool operator==(const self& oth) { return node == oth.node; };
bool operator!=(const self& oth) { return !(node == oth.node); };
};
template
class mycom {
public:
bool operator() (const T& left, const T& right) {
return left < right;
}
};
template>
class RBTree {
public:
typedef rbtree_iterator iterator;
typedef rbtree_iterator const_iterator;
RBTree(const keycompare& comp = keycompare());//默认构造函数
RBTree(const RBTree& oth);
RBTree(RBTree&& oth);//移动构造函数
template
RBTree(_itr begin, _itr end);//如果传入的是迭代器
virtual~RBTree();
///操作符重载
RBTree& operator=(const RBTree& oth);
RBTree& operator=(RBTree&& oth);
const_iterator operator[](int i);
void clear();//清空树
int size();//返回树的数量
bool empty();//判空
int count(T val);//与val相等的元素的数量
void insert(T value);//插入元素,可重复
void insert_unique(T value);//插入元素,如果重复,后来的会代替现在的。
//想怎么删除,就怎么删除
void erase_unique(T value);//删除一个等于value的元素
void erase_equal(T value); //删除所有等于value的元素
void erase(const_iterator& l, const_iterator& r);//删除迭代器区间的元素
void erase(const_iterator& start, int n);//删除从指定迭代器开始的n个元素
void erase(const_iterator& it);//删除迭代器指向的元素
////你要的迭代器 我都有
const_iterator begin();//返回最小元素的迭代器
const_iterator end(); //返回一个边界,跟STL其他容器的end()差不多
const_iterator back(); //返回最大元素的迭代器
const_iterator start(); //返回一个边界,只不过是前边界,实际与end()地址相同,为了区别写了这么个函数
const_iterator find(T val);//寻找指定元素第一次出现,返回一个迭代器;
const_iterator findlast(T val);//寻找指定元素最后一次出现,返回一个迭代器;
const_iterator lower_bound(T val);//如果元素存在,返回它第一次出现时的迭代器,若不存在返回它下边界迭代器
const_iterator upper_bound(T val);//元素存在,返回最后一次出现迭代器,不存在,返回上边界迭代器
//测试用的函数
void pre_traverse();
void mid_traverse(T* pt = NULL);
void post_traverse();
void print();
RB_TreeNode* guard();
RB_TreeNode * getroot();
private:
void RBerase(RB_TreeNode* node);//删除函数
RB_TreeNode* RBTree_serch(T value, RB_TreeNode* beginRoot);//寻找函数
void RBTree_insert(T value, bool isunique);//插入函数
void Left_Rotate(RB_TreeNode * t);//左旋
void Right_Rotate(RB_TreeNode * t);//右旋
void RBDELETE_Fixup(RB_TreeNode * t);//删除后调整红黑特性
void RBINSERT_Fixup(RB_TreeNode * t);//插入后调整红黑特性
RB_TreeNode* findFront(RB_TreeNode* t);//寻找前驱
RB_TreeNode* findSucceed(RB_TreeNode * t);//寻找后继
const_iterator findFandB(RB_TreeNode* target, bool isForward);//寻找与指定值相等的最前面元素与最后面元素
RB_TreeNode * copyTree(const RB_TreeNode* oth, RB_TreeNode* p, RB_TreeNode*const& Guard);//拷贝
void distroy(RB_TreeNode* root);//销毁
int findindex(RB_TreeNode* root, int num, RB_TreeNode*& tar, int& index);
void relationGuard();
void printNode(RB_TreeNode * outT);
RB_TreeNode* _guard;
RB_TreeNode* _root;
int _len;
keycompare com;
friend ostream& operator<<(ostream &out, RB_TreeNode * outT);
};
////析构函数与构造函数
template>
RBTree::RBTree(const keycompare& comp=keycompare()):_len(0),com(comp), _guard(new RB_TreeNode())
{
_guard->left = _guard;
_guard->right = _guard;
_root = _guard;
}
template>
RBTree::RBTree(const RBTree& oth):_guard(new RB_TreeNode(0)),_len(oth._len){
_root = copyTree(oth._root, _guard,oth._guard);
relationGuard();
}
//移动构造函数
template>
RBTree::RBTree(RBTree&& oth):_len(oth._len), _guard(oth._guard), _root(oth._root) {
oth._root = NULL;
oth._guard = NULL;
}
template>
template
RBTree::RBTree(_itr begin,_itr end):_len(0), com(keycompare()), _guard(new RB_TreeNode()), _root(_guard) {
while (begin != end)
insert(*(begin++));
}
template>
RBTree::~RBTree()
{
clear();
}
////操作符重载
template>
RBTree& RBTree::operator=(const RBTree& oth) {
clear();//在拷贝之前,先要释放已有资源
_root = copyTree(oth._root, _guard);
return *this;
}
//当参数为右值引用
template>
RBTree& RBTree::operator=(RBTree&& oth) {
clear();//在指向传进来的节点之前,要先释放原来的资源;
_root = oth.root;
return *this;
}
template>
rbtree_iterator RBTree::operator[] (int i) {
if (i<0||i >= _len) {
cout << "访问越界" << endl;
throw;
}
RB_TreeNode* tar = NULL;
findindex(_root, 0, tar, ++i);
return const_iterator(tar, _guard);
}
//判空,size,清除
template>
void RBTree::clear() {
if (_root == NULL)
{
_len = 0;
return;
}
distroy(_root);
_root = NULL;
_len = 0;
}
template>
int RBTree::size()
{
return _len;
}
template>
bool RBTree::empty() {
return _len == 0;
}
template>
int RBTree::count(T val) {
RB_TreeNode* pval = RBTree_serch(val,_root);
if (pval == _guard) return 0;
int num=1;
RB_TreeNode* temp = pval->findFront(_guard);
while (temp!=_guard&&!com(temp->key, val) && !com(val, temp->key)) {
temp = temp->findFront(_guard);
++num;
}
temp = pval->findSucceed(_guard);
while (temp != _guard && !com(temp->key, val) && !com(val, temp->key)) {
temp = temp->findSucceed(_guard);
++num;
}
return num;
}
////各种插入与删除操作
template>
void RBTree::insert(T value)
{
RBTree_insert(value, false);
}
template>
void RBTree::insert_unique(T value) {
RBTree_insert(value, true);
}
template>
void RBTree::erase_unique(T value) {
RBerase(RBTree_serch(value, _root));
}
template>
void RBTree::erase_equal(T value) {
RB_TreeNode* tar = RBTree_serch(value, _root);
if (tar == _guard) return;
erase(findFandB(tar, true), ++findFandB(tar, false));
}
template>
void RBTree::erase(const_iterator& l, const_iterator& r) {
while (l != r&&l != end())
erase(l++);
}
template>
void RBTree::erase(const_iterator& start, int n) {
while (n--&&start != end()) erase(start++);
}
template>
void RBTree::erase(const_iterator& it) {
RBerase(it.node);
}
///必要的迭代器
template>
rbtree_iterator RBTree::begin() {
return const_iterator(_guard->left, _guard);
}
template>
rbtree_iterator RBTree::end() {
return const_iterator(_guard, _guard);
}
template>
rbtree_iterator RBTree::back() {
return const_iterator(_guard->right, _guard);
}
template>
rbtree_iterator RBTree::start() {
return end();
}
template>
rbtree_iterator RBTree::find(T val) {
RB_TreeNode* p = RBTree_serch(val, _root);
if (p == _guard) return end();
return findFandB(p, true);
}
template>
rbtree_iterator RBTree::findlast(T val) {
RB_TreeNode* p = RBTree_serch(val, _root);
if (p == _guard) return end();
return findFandB(p, false);
}
template>
rbtree_iterator RBTree::lower_bound(T val) {
if (_len == 0) return end();
RB_TreeNode* root = _root;
RB_TreeNode* tmp = NULL;
while (root != _guard) {
tmp = root;
if (com(val, root->key))
root = root->left;
else if (com(root->key, val))
root = root->right;
else {
tmp = root;
break;
}
}
if (root == _guard) {
if (com(tmp->key, val))
return const_iterator(findSucceed(tmp), _guard);
else
return const_iterator(tmp, _guard);
}
/*if (root == _guard) {
if (com(val, tmp->key))
return const_iterator(findFront(tmp), _guard);
else
return const_iterator(tmp, _guard);
}*/
return findFandB(tmp, true);
}
template>
rbtree_iterator RBTree::upper_bound(T val) {
RB_TreeNode* root = _root;
RB_TreeNode* tmp = NULL;
while (root != _guard) {
tmp = root;
if (com(val, root->key))
root = root->left;
else if (com(root->key, val))
root = root->right;
else {
tmp = root;
break;
}
}
if (root == _guard) {
if (com(tmp->key, val))
return const_iterator(findSucceed(tmp), _guard);
else
return const_iterator(tmp, _guard);
}
return findFandB(tmp, false);
}
////后面都是为了实现前面的接口的内部函数
template>
RB_TreeNode* RBTree::guard() {
return _guard;
}
template>
RB_TreeNode * RBTree::getroot()
{
return _root;
}
///插入节点后调整红黑树满足红黑树五个特性
//////左旋
template>
void RBTree::Left_Rotate(RB_TreeNode * t)
{
if (t == _guard || t->right == _guard)
{
cout << "左旋节点或节点右孩子不应为空" << endl;
return;
}
RB_TreeNode * tmp = t->right;
tmp->p = t->p;
if (t->p == _guard)
{
_root = tmp;
}
else if (t == t->p->left)
t->p->left = tmp;
else
t->p->right = tmp;
t->p = tmp;
t->right = tmp->left;
if (tmp->left != _guard)
tmp->left->p = t;
tmp->left = t;
}
////右旋
template>
void RBTree::Right_Rotate(RB_TreeNode * t)
{
if (!t || !t->left)
{
cout << "右旋节点或节点左孩子不应为空" << endl;
return;
}
RB_TreeNode * tmp = t->left;
tmp->p = t->p;
if (t->p == _guard)
{
_root = tmp;
}
else if (t == t->p->left)
t->p->left = tmp;
else
t->p->right = tmp;
t->p = tmp;
t->left = tmp->right;
if (tmp->right != _guard)
tmp->right->p = t;
tmp->right = t;
}
template>
void RBTree::RBTree_insert(T value, bool isunique) {
RB_TreeNode * Tnode = new RB_TreeNode(value, _guard);
RB_TreeNode * tmp = _root;
RB_TreeNode * Ptmp = _guard;
if (_root == _guard)//如果插入的节点是第一个节点,相当于初始化根节点
{
_root = Tnode;
_root->color = black;
_len++;
_guard->left = _root;
_guard->right = _root;
return;
}
//如果插入的节点小于最小值,直接插在最左边,并更新最小值节点
if (com(value, _guard->left->key)) {
Tnode->p = _guard->left;
_guard->left->left = Tnode;
_guard->left = Tnode;
}
//如果插入的节点大于等于最大值,直接插在最右边,更新最大值节点
else if (!com(value, _guard->right->key)) {
if (!com(_guard->right->key, value) && isunique) {
swap(_guard->right->key, tmp->key);
delete Tnode;
return;
}
Tnode->p = _guard->right;
_guard->right->right = Tnode;
_guard->right = Tnode;
}
else {
///以上三种情况都不属于寻找合适的插入节点,就一直往下找,直到边界条件(找到了空节点,在我这是_guard)
while (tmp != _guard)
{
Ptmp = tmp;
if (com(value, tmp->key))
tmp = tmp->left;
else if (com(tmp->key, value))
tmp = tmp->right;
else {
if (isunique) {
swap(Tnode->key, tmp->key);
delete Tnode;
return;
}
tmp = tmp->right;
}
}
///更新插入节点的父亲,以及该父亲的孩子须得指向它
Tnode->p = Ptmp;
if (com(Tnode->key, Ptmp->key))
Ptmp->left = Tnode;
else
Ptmp->right = Tnode;
}
///如果插入节点的父亲是红色,则违反了红黑树红黑特性,需要调整
if (Tnode->p->color == red)
RBINSERT_Fixup(Tnode);
_len++;///别忘了更新元素数目
}
/////删除节点
template>
void RBTree::RBerase(RB_TreeNode* node)
{
RB_TreeNode* t = node;
if (t == _guard) return;
RB_TreeNode* tmp;
if (t->left != _guard&&t->right != _guard)
{
///节点左右孩子都不是边界节点,交换信息
RB_TreeNode* succ = findSucceed(t);
if (t == t->p->left)
t->p->left = succ;
else
t->p->right = succ;
if (succ == succ->p->left)
succ->p->left = t;
else
succ->p->right = t;
swap(succ->p, t->p);
swap(succ->color, t->color);
swap(succ->left, t->left);
swap(succ->right, t->right);
succ->left->p = succ;
succ->right->p = succ;
if (t->left != _guard)
t->left->p = t;
if (t->right != _guard)
t->right->p = t;
if (succ->p == _guard) _root = succ;
//这里其实最简单的方法是交换两个key然后直接删除succ,但是这样做会令原来指向succ的迭代器失效,所以要逐个交换。
}
///这里就是的tmp就是我们需要跟删除节点父亲建立联系的孩子节点
if (t->left == _guard)
tmp = t->right;
else
tmp = t->left;
///如果删除的是最小值或者最大值,要更新_guard孩子
if (t == _guard->left)
_guard->left = findSucceed(t);
if (t == _guard->right)
_guard->right = findFront(t);
if (t->p == _guard) _root = tmp;///如果是根,要及时更新根节点
else if (t == t->p->left)///tmp 取代原来t的位置
t->p->left = tmp;
else
t->p->right = tmp;
tmp->p = t->p;
if (t->color == black)///如果被删除的节点是黑色,需要调整红黑树红黑特性
{
RBDELETE_Fixup(tmp);
}
_len--;
delete t;
t = NULL;
return;
}
template>
void RBTree::RBINSERT_Fixup(RB_TreeNode * t)
{
RB_TreeNode * tmp = t->p;
RB_TreeNode * Pbra = _guard;
//出递归的条件,就是节点是黑色
while (tmp->color==red)
{
//这个就是我们讨论的当插入节点是左孩子的时候
if (tmp == tmp->p->left)
{
Pbra = tmp->p->right;
///第一种情况 叔叔是红色
if (Pbra!=_guard&&Pbra->color == red)
{
tmp->color = black;
tmp->p->color = red;
Pbra->color = black;
t = tmp->p;//当前节点变成祖父节点
tmp = t->p;//同时更新父节点为祖父节点的父亲节点
}
else
{
///叔叔是黑色
if (t == tmp->right)///插入节点是右孩子,左旋,变成第三种情况
{
Left_Rotate(tmp);
tmp = t;//注意这里左旋之后,孩子父亲角色换了
}
tmp->color = black;//tmp颜色变黑,达到出循环的条件
tmp->p->color = red;
Right_Rotate(tmp->p);
}
}
else//这边是插入节点是右孩子的情况,与上面是对称的
{
Pbra = tmp->p->left;
if (Pbra!=_guard&&Pbra->color == red)
{
tmp->color = black;
tmp->p->color = red;
Pbra->color = black;
t = tmp->p;
tmp = t->p;
}
else
{
if (t == tmp->left)
{
Right_Rotate(tmp);
tmp = t;
}
tmp->color = black;
tmp->p->color = red;
Left_Rotate(tmp->p);
}
}
if (tmp==_guard)//说明当前节点是根节点,因为只有根节点的父节点是边界节点_guard
{
///这里其实可以直接将t的颜色变成黑色,然后直接出循环
tmp = t;
tmp->color = black;
}
}
}
///删除节点后调整红黑树满足红黑树五个特性
template>
void RBTree::RBDELETE_Fixup(RB_TreeNode * t)
{
RB_TreeNode * w = NULL;
while (t!=_root&&t->color==black)
{
////要连接的孩子节点是左孩子的情况
if (t == t->p->left)
{
w = t->p->right;//兄弟节点
////第一种情况
if (w->color == red)///兄弟是红色
{
///交换兄弟与父亲的颜色,因为兄弟颜色是红色,父亲肯定是黑色,所以直接赋值
t->p->color = red;
w->color = black;
Left_Rotate(t->p);
}
else
{
///兄弟右孩子是红色,就是第四种情况
if (w->right->color == red)
{
w->color = t->p->color;
t->p->color = black;
w->right->color = black;
Left_Rotate(t->p);
t = _root;///这里是为了最后一句,出循环后要把t染成黑色,将t设置为根节点,可以出循环,将根节点染黑没影响
}
else if (w->left->color == red)///右孩子是黑色,左孩子红色
{
w->color = red;
w->left->color = black;
Right_Rotate(w);
w = w->p;//别忘了右旋之后更新兄弟
}
else
{
///两个孩子都是黑色,也就是第二种情况,把兄弟染红,将另一重黑色往上推,也就是父亲节点
w->color = red;
t = t->p;
}
}
}
///这边是当前节点是右孩子的情况 完全对称
else
{
w = t->p->left;
if (w->color == red)
{
t->p->color = red;
w->color = black;
Right_Rotate(t->p);
}
else
{
if (w->left != _guard&&w->left->color == red)
{
w->color = t->p->color;
t->p->color = black;
w->left->color = black;
Right_Rotate(t->p);
t = _root;
}
else if (w->right != _guard&&w->right->color == red)
{
w->color = red;
w->right->color = black;
Left_Rotate(w);
w = w->p;
}
else
{
w->color = red;
t = t->p;
}
}
}
}
t->color = black;///出循环要么是t是红色,要么t是根节点,直接染黑。
}
/////以beginRoot为根的子树开始寻找节点
template>
RB_TreeNode* RBTree::RBTree_serch(T value, RB_TreeNode* beginRoot)
{
RB_TreeNode * p = beginRoot;
while (p != _guard)
{
if (com(p->key, value))
p = p->right;
else if (com(value, p->key))
p = p->left;
else
return p;
}
return p;
}
///////寻找前驱
template>
RB_TreeNode* RBTree::findFront(RB_TreeNode* t)
{
return t->findFront(_guard);
}
/////寻找后继
template>
RB_TreeNode* RBTree::findSucceed(RB_TreeNode * t)
{
return t->findSucceed(_guard);
}
template>
RB_TreeNode * RBTree::copyTree(const RB_TreeNode* oth, RB_TreeNode* p, RB_TreeNode* const& Guard) {
if (oth == Guard) return _guard;
RB_TreeNode* root = new RB_TreeNode(oth->key);
root->color = oth->color;
root->p = p;
root->left = copyTree(oth->left, root, Guard);
root->right = copyTree(oth->right, root, Guard);
return root;
}
template>
void RBTree::distroy(RB_TreeNode* root) {
if (root == _guard) return;
distroy(root->left);
distroy(root->right);
delete root;
}
template>
int RBTree::findindex(RB_TreeNode* root, int num, RB_TreeNode*& tar, int& index) {
if (tar != NULL || root == _guard) return 0;
int l = findindex(root->left, 0, tar, index);
if (l + 1 + num == index) {
tar = root;
return 0;
}
int r = findindex(root->right, l + 1+num, tar, index);
return l + r + 1;
}
template>
rbtree_iterator RBTree::findFandB(RB_TreeNode* target, bool isForward) {
if (isForward) {
RB_TreeNode* succ = findFront(target);
while (succ != _guard && !com(target->key, succ->key) && !com(succ->key, target->key)) {
target = succ;
succ = findFront(succ);
}
return const_iterator(target, _guard);
}
else {
RB_TreeNode* succ = findSucceed(target);
while (succ != _guard && !com(target->key, succ->key) && !com(succ->key, target->key)) {
target = succ;
succ = findSucceed(succ);
}
return const_iterator(target, _guard);
}
return const_iterator(_guard, _guard);
}
template>
void RBTree::relationGuard() {
RB_TreeNode* node = _root;
while (node->left != _guard)
node = node->left;
_guard->left = node;
node = _root;
while (node->right != _guard)
node = node->right;
_guard->right = node;
}
////自己测试用的函数
/////前序遍历
template>
void RBTree::pre_traverse()
{
}
////中序遍历
template>
void RBTree::mid_traverse(T* pt=NULL)
{
RB_TreeNode * p = _root;
stack *> s;
while (true)
{
while (p!=_guard)
{
s.push(p);
p = p->left;
}
if (s.empty()) break;
p = s.top();
if (pt == NULL)
cout << p->key << " ";
else
*(pt++) = p->key;
s.pop();
p = p->right;
}
cout << endl;
}
/////后序遍历
template>
void RBTree::post_traverse() {
}
/////层次遍历
template>
void RBTree::print() {
RB_TreeNode * pRoot = _root;
if (!pRoot) return;
vector *> > vq(2);
int index = 0;
vq[index].push(pRoot);
while (!vq[index].empty() || !vq[1 - index].empty())
{
while (!vq[index].empty())
{
RB_TreeNode * p = vq[index].front();
vq[index].pop();
printNode(p);
if (p->left != _guard)
vq[1 - index].push(p->left);
if (p->right != _guard)
vq[1 - index].push(p->right);
}
cout << endl;
index = 1 - index;
}
}
template>
void RBTree::printNode(RB_TreeNode * outT) {
if (outT == _guard) return ;
cout << "[" << " " << "key:" << outT->key << " ";
if (outT->color == 0)
cout << "color:" << "red" << " ";
else
cout << "color:" << "black" << " ";
if (outT->left != _guard)
cout << "left:" << outT->left->key << " ";
if (outT->right != _guard)
cout << "right:" << outT->right->key << " ";
if (outT->p != _guard)
cout << "parent:" << outT->p->key << " ";
cout << "]";
}
template
ostream& operator<<(ostream &out, RB_TreeNode * outT)
{
return out;
}