/*红黑树:一种二叉查找树,某种程度上也是一种平衡树,其中平衡树还有avl树。
红黑树:对于红黑树的而言,首先是一个二叉查找树,此外每一个节点包含的还有一项技术指标就是
每个节点还有一个表示该节点颜色的项目,节点要么是红色要么是黑色,为了限制红黑树的平衡型,通过
使各个分支上的红黑的着色方式达到一种近似的平衡,所以红黑树算是一种平衡树,类似的平衡树还有:avl树,treap树;
红黑树的特性:(1)每一个节点颜色:红或者黑;
(2)根节点是黑色的;
(3)一个节点为红,子节点为黑;
(4)每一个叶子节点(算法导论里面的意思为非含有键值节点)为黑;
(5)从某一节点起到叶子节点为止,各个分支的黑色的节点个数一样。(黑色的个数也叫黑高度,一个树的黑高度就是从树的根节点开始到叶子节点。
红黑树的基本动态操作是O(lgn)
*/
#include <iostream>
#define RED 1
#define BLACK 0
using namespace std;
struct node
{
int key;
node* p;
node* left;
node* right;
bool color;
node(){
key=0;
p=NULL;
left=NULL;
right=NULL;
color=RED;
}
node(int k):key(k){
p=NULL;
left=NULL;
right=NULL;
color=RED;
}
node& operator=(node& other)
{
if(this!=&other)
{
this->key=other.key;
this->p=other.p;
this->color=other.color;
this->left=other.left;
this->right=other.right;
}
return *this;
}
};
struct rbtree
{
node* root;//根
node* nil;//哨兵
rbtree()
{
int k;
cin>>k;
root=new node(k);
root->color=BLACK;
nil=new node(100000);
nil->color=BLACK;
root->p=nil;//一开始根节点就指向该值
root->left=nil;//最初的时候如此的进行设计。
root->right=nil;
}
void letf_rotate(node*);//o(1)
void right_rotate(node*);//o(1)
void rb_insert_fixup(node*);
void rb_insert(node*);//o(lgn)
void rb_delete_fixup(node*);
node* rb_delete(node*);//o(lgn)
node* rb_successor(node*);//辅助
node* rb_min(node*);//辅助
};
void rbtree::letf_rotate(node* px)
{
node* py=px->right;
if(py!=nil)
{
px->right=py->left;
if(py->left!=nil)
py->left->p=px;
py->p=px->p;
if(px==nil)
root=py;
if(px==px->p->left)
px->p->left=py;
else
px->p->right=py;
py->left=px;
px->p=py;
}
else
{
cerr<<"left of px is null "<<endl;
return ;//一般情况下函数返回值为void 可以如此;
}
cout<<"finished! "<<endl;
}
void rbtree::right_rotate(node* px)//13.2-1
{
node* py=px->left;
if(py!=nil)
{
px->left=py->right;
if(py->right!=nil)
py->right->p=px;
py->p=px->p;
if(px->p==nil)
root=py;
if(px==px->p->left)
px->p->left=py;
else
px->p->right=py;
py->right=px;
px->p=py;
}else
{
cerr<<"right of px is nil "<<endl;
return ;
}
cout<<"finishede!"<<endl;
}
/************rbtree_insert()************************/
void rbtree::rb_insert_fixup(node* pz)
{
node* py=new node();
while(pz->p->color==RED)//对于红黑树,插入一个点为红,只要保证最后的树是满足红黑树的五个性质即可,插入的是红点,黑高度不影响,那么插入
{//红点,会造成的影响是:如果插入的点成为根节点的话;以及插入节点的父节点为红。这里以插入节点的父节点为红做判断。
if(pz->p==pz->p->p->left)
{
py=pz->p->p->right;//py为pz的叔叔
if(py->color==RED)//情况1:叔叔为红的时候
{
pz->p->color=BLACK;
py->color=BLACK;
pz->p->p->color=RED;//修改颜色使父辈变黑,爷爷变红,这样节点黑高度不受影响,原因是pz为红,pz父辈为红,那么对应必定有祖父辈,插入前的rbtree为
//正常的,父辈不会为根,那么存在祖父辈,如果祖父辈为红,父辈怎么会为红呢?祖父辈必定为黑以保证插入前的rbtree是正常。
pz=pz->p->p;
}else
{
if(pz==pz->p->right)//情况2:叔叔为黑,且pz是右孩子
{
pz=pz->p;
letf_rotate(pz);
}
pz->p->color=BLACK;//情况3:叔叔为黑,pz是左孩子
pz->p->p->color=RED;
right_rotate(pz->p->p);
}
}else if(pz->p==pz->p->p->right)
{
py=pz->p->p->left;//叔叔
if(py->color==RED)
{
pz->p->color=BLACK;
py->color=BLACK;
pz->p->p->color=RED;
pz=pz->p->p;
}else
{
if(pz==pz->p->left)
{
pz=pz->p;
right_rotate(pz);
}
pz->p->color=BLACK;
pz->p->p->color=RED;
letf_rotate(pz->p->p);
}
}
}
root->color=BLACK;//这里的做法,可以避免插入的节点成为根节点,黑高度还是不受影响,只是大了一个
// delete py;
}
/**
插入算法理解:插入的pz为红,这样可以避免节点黑高度的影响,以pz的父节点为黑与否作为循环的标准判定,这时候只存在一种意外情况即:根节点是pz,pz的父为黑的,那么的话
就会影响rbtree的属性,但是最终结尾的时候我们有一个修改根的颜色可以保证,此外当pz的父节点是红的时候,我们可以根据上面的情况判断存在祖父节点,且为黑,此外的话
pz,pz的父节点为红,这是基本的属性情况,下面分三种也就是算法导论所述的情况,即叔叔节点为红,叔叔节点为黑时候pz为右子树和左子树的三种情况
情况1:为了使红黑树的五个性质满足,从祖父点开始,父辈那层为红,为了避免pz,pz父辈的同色,修改颜色即可,这样的话祖父为红,是唯一的纰漏,相应的向上回溯即可:
情况2:对于这种父辈不同色的情况是存在的,比如叔叔的是一个nil,父为红,接着接nil,为了满足红黑树的五个属性根据上面分析父,祖父颜色还是那样的,为了下一步使黑高度
满足,且pz,pz父颜色不同,进行了所谓的旋转。
时间复杂度:由于一开始的insert()是对一颗二叉树插入一个点,那么一般情况下的话就是树的高度o(lgn),后面的rb_insert_fixup()主要是一个while也是while树的高度
所以时间复杂度是o(lgn)
**/
void rbtree::rb_insert(node* pz)//红黑树是二叉查找树,插入节点先图红,可以保证节点黑高度,插入的是类似二叉查找树的插入
{
node* py=nil;
node* px=root;
while(px!=nil)
{
py=px;
if(pz->key>px->key)
px=px->right;
else
px=px->left;
}
pz->p=py;
if(py==nil)
root=pz;
else
if(pz->key<py->key)
py->left=pz;
else
py->right=pz;
pz->left=nil;
pz->right=nil;
pz->color=RED;
rb_insert_fixup(pz);
// delete py;
// delete px;
}
/****************习题******************************/
//13.3-1 ans:会影响节点黑高度
/*13.3-2 ans:
38(black)
19(red) 41(black)
12(black) 31(black)
8(red)
*/
/******************rbtree_delete()*******************************/
node* rbtree::rb_min(node* pz)
{
while (pz->left!=nil)
{
pz=pz->left;
}
return pz;
}
node* rbtree::rb_successor(node* pz)//中序遍历求后继
{
node* py=new node();
if(pz->right!=nil)
return rb_min(pz->right);
py=pz->p;
while (py!=nil&&pz==py->right)
{
pz=py;
py=py->p;
}
return py;
}
void rbtree::rb_delete_fixup(node* pz)
{/*
这个函数用来修改删除一个黑节点之后的子树,因为如果是删除红色,木有影响但是删除的是黑色的话就会有影响比如说黑高度影响,以及删除之后
会造成父子同为红色的可能性以及根节点为红色等可能性,所以需要进行调整,有一个while循环,循环会造成向上回溯,回溯到最终的终结的话就是
要么到达根节点,要么是节点颜色属性是红的(这样为了满足黑高度可以把这个点染黑),根据二叉树的删除来计算的话,删除的那个点的下一个点必
为单个的情况,要么左子树,要么右子树,要么无子树,相应的作为判断的pz点,就可以用其颜色作为while标准,为红会后续的直接涂黑(直接增加黑高度)
为黑的话,就要进行while的操作。
*/
node* pw=new node();
while(pz!=root&&pz->color==BLACK)//为根节点,整体黑高度不影响,为红,后面会涂黑。
{
if(pz==pz->p->left)
{
pw=pz->p->right;
if(pw->color==RED)//只有是父节点是黑色的情况pw才可能是红色。
{
pz->p->color=RED;
pw->color=BLACK;
letf_rotate(pz->p);
pw=pz->p->right;
}//情况1
if (pw->left->color==BLACK&&pw->right->color==BLACK)
{
pw->color=RED;//只用堆pw修改,pz实际上是黑的,去掉一重还是黑,不要改,但是pw要;
pz=pz->p;//此操作结束后,pz为红,结束并涂黑,如果pz为黑,还要pz加一重黑
}//情况2:去掉一重黑,让存在双重黑的pz变正常,如果是从情况1来,就是pz为红的了,如果不是的话,pz为黑,按照算法导论所述,pz让存在
//双重黑,主要是保证该树黑高度,从pz节点起的黑高度和没有删某个黑节点一样的,主要是pz的父那个节点开始少一个黑高度。
else
{
if(pw->right->color==BLACK)//情况3
{
pw->left->color=BLACK;
pw->color=RED;
right_rotate(pw);
pw=pz->p->right;
}//情况4
pw->color=pz->p->color;
pz->p->color=BLACK;
pw->right->color=BLACK;
letf_rotate(pz->p);
pz=root;
}
}
else if(pz==pz->p->right)
{
pw=pz->p->left;//叔叔
if(pw->color=RED)
{
pz->p->color=RED;
pw->color=BLACK;
right_rotate(pz->p);
pw=pz->p->left;
}
if(pw->left->color==BLACK&&pw->right->color==BLACK)
{
pw->color=RED;
pz=pz->p;
}
else
{
if(pw->right->color==BLACK)
{
pw->left->color=BLACK;
pw->color=RED;
letf_rotate(pw);
pw=pz->p->left;
}
pw->color=pz->p->color;
pz->p->color=BLACK;
pw->right->color=BLACK;
right_rotate(pz->p);
pz=root;//只要到达情况4就可以完成了,因为已经添加一个黑高度,所以用此结束循环。
}
}
}
pz->color=BLACK;
// delete pw;
//上面的四种情况不一定是按照顺序来的,但是最终的解决都是通过情况4进行解决,前面三种情况通过转换最后都会变为情况4,那么相应的
//对于情况4,而言我们本身是需要pz(删除的节点的下一个点),含有2重黑色,但是实际上是要求pz上面一层添加一个黑色,情况4就是如此做
//通过把pw的颜色换pz的父颜色,父色变黑,在左旋,那么造成,pz的上面一层为父(黑),上面一层为pw原来颜色(父变黑前色),pw的右子树由
//红变黑,那么这边对应黑高度不变。无论是那种情况最终都是变为情况四处理的。
}
/*
时间复杂度:o(lg(n))
*/
node* rbtree::rb_delete(node* pz)
{
node* py=new node();
node* px=new node();
if (pz->left==nil||pz->right==nil)//pz这节点有单个左或者单个右或者全无,反就是有左有右
py=pz;
else
py=rb_successor(pz);//如果是有左右子树,就是求后继,下面的操作无论是对于后继还是非后继都是一样的。
if(py->left!=nil)
px=py->left;
else
px=py->right;
px->p=py->p;
if(py==nil)
root=px;
else
if(py==py->p->left)
py->p->left=px;
else
py->p->right=px;
if(py!=pz)
pz->key=py->key;
if (py->color==BLACK)//若是一个黑的话,会影响黑高度,所以要调整
{
rb_delete_fixup(px);
}
// delete py;
// delete px;
return py;
}
//
/*********************main_function()************************************/
int main()
{
rbtree* rb=new rbtree();
node* p1=new node(1);
node* p2=new node(2);
node* p3=new node(3);
// rb->letf_rotate(p1);//一开始运行此处的时候会报错,原因很简单,在上面的py->left等都是空的。所以赋值的时候会出错的。勿怪了
// rb->right_rotate(p2);
rb->rb_insert(p1);
rb->rb_insert(p2);
rb->rb_insert(p3);
rb->rb_delete(p2);
delete p1;
delete p2;
delete p3;
return 0;
}
/*
stl容器使用rbtree为底层实现的细节解释:
*/