一、红黑树的定义
1.红黑树
红黑树是每个结点都带有颜色属性的二叉查找树,每个结点为两种颜色之一:红色或黑色。除了二叉查找树的一般要求外,为了保持平衡性,红黑树增加了对结点颜色设置的限制条件:
(1)每个结点为红色或黑色
(2)根结点是黑色
(3)每个红色结点的两个子结点都是黑色
(4)从任一结点到其子树中的每个叶结点的路径上都包含相同数目的黑色结点
2.类型定义
//红黑树的类型定义
enum color{RED,BLACK};
typedef struct rbtNode {
int key;
color c;//当前结点的颜色
rbtNode* lc, * rc, * pa;//pa为指向当前结点父结点的指针
rbtNode():c(RED),lc(NULL),rc(NULL),pa(NULL){}
};
二、红黑树的操作
1.插入操作
1.算法1-1:红黑树的插入
功能
在红黑树t中,插入一个键值为k的结点
算法步骤
利用bst插入结点方法在t中插入一个结点C,将结点C的颜色设置为红色,假设C的叔叔结点为U,C的父结点为B,B的父结点为A,分为以下情况
(1)C为根节点,则直接将结点C设置为黑色
(2)B为黑色,则满足所有约束条件,t仍是红黑树,插入结束
(3)B为红色,U为红色,将B和U都设置为黑色,将A设置为红色,再将A看成新插入的结点C,重复1~4检查结点A,直到A的父结点为黑色或A为根结点
(4)B为红色,但叔叔结点U为黑色或为空树,根据A,B,C的不同关系,可分为以下情况:
(4.1)B为左孩子,C为左孩子:对A,B进行LL旋转,并将B设置为黑色,A设置为红色
(4.2)B为左孩子,C为右孩子:对A,B,C进行LR旋转,并将C设置为黑色,A设置为红色
(4.3)B为右孩子,C为右孩子:对A,B进行RR旋转,并将B设置为黑色,A设置为红色
(4.4)B为右孩子,C为左孩子:对A,B,C进行RL旋转,并将C设置为黑色,A设置为红色
//检查红黑树t中的结点C是否符合红黑树的条件,如果不符合,则调整结构和颜色
void adjust_color(rbTree& t, rbTree& C) {
rbTree rt = NULL;
if (C->c == RED) {
if (C->pa == NULL) {//情形1
set_color(C, BLACK), t = C;
return;
}
rbTree B = C->pa;
if (B->c != BLACK) {//情形2
rbTree A = B->pa;
if (B == A->lc) {
if (A->rc == NULL || A->rc->c == BLACK) {//U为A->rc
if (C == B->lc) {//情形(4.1)
rt = LL(A);
set_color(A, RED, B, BLACK);
}
else {//情形(4.2)
rt = LR(A);
set_color(A, RED, C, BLACK);
}
}
else {
set_color(B, BLACK, A->rc, BLACK, A, RED);
adjust_color(t, A);//A为红色,检查A
}
}
else {
if (A->lc == NULL || A->lc->c == BLACK) {//U为A->lc
if (C == B->rc) {
rt = RR(A);
set_color(A, RED, B, BLACK);
}
else {
rt = RL(A);
set_color(A, RED, C, BLACK);
}
}
else {
set_color(B, BLACK, A->lc, BLACK, A, RED);
adjust_color(t, A);//A为红色,检查A
}
}
}
if (rt && rt->pa == NULL)t = rt;//更新根结点
}
}
//向红黑树t中插入键值为k的结点,插入成功,返回true,否则,返回false
bool rbt_insert(rbTree& t, int k) {
rbTree C, t1 = t, pa = NULL;
int type;//插入结点的左右性,0:插入左子树,1:插入右子树
C = new rbtNode;
C->key = k;//新插入结点,颜色为红色
if (t == NULL) {
t = C;
set_color(C, BLACK);
return true;
}
while (t1) {
pa = t1;//记录当前结点的父结点
if (t1->key > k)t1 = t1->lc, type = 0;//插入左子树
else if (t1->key < k)t1 = t1->rc, type = 1;//插入右子树
else return false;
}
C->pa = pa;//设置p与C的父子关系
if (type == 0)pa->lc = C;
else pa->rc = C;
adjust_color(t, C);//对树的结构和颜色进行调整
return true;
}
2.红黑树的旋转操作
//红黑树的LL旋转操作,返回旋转后子树的根结点
rbTree LL(rbTree A) {
rbTree B = A->lc;
if (B->rc)B->rc->pa = A;
if (A->pa) {//将B设置为A的父结点的孩子结点
if (A->pa->lc == A)A->pa->lc = B;//B为A的父结点的左孩子
else A->pa->rc = B;//B为A的父结点的右孩子
}
A->lc = B->rc;
B->rc = A;
B->pa = A->pa;
A->pa = B;//其他结点关系的变化
return B;
}
//红黑树的RR旋转
rbTree RR(rbTree A) {
rbTree B = A->rc;
if (B->lc)B->lc->pa = A;
if (A->pa) {
if (A->pa->lc == A)A->pa->lc = B;
else A->pa->rc = B;
}
A->rc = B->lc;
B->lc = A;
B->pa = A->pa;
A->pa = B;
return B;
}
//红黑树的LR旋转操作
rbTree LR(rbTree A) {
A->lc = RR(A->lc);//RR旋转
return LL(A);
}
//红黑树的RL旋转操作
rbTree RL(rbTree A) {
A->rc = LL(A->rc);//LL旋转
return RR(A);//RR旋转
}
2.删除操作
算法1-2:红黑树的删除操作
功能
在红黑树t中,删除一个键值为k的结点
算法步骤
删除键值为k的结点,假设最终删除的结点为叶结点tmp。如果tmp为红色,则直接删除,否则根据tmp对红黑树进行调整,并根据需要检查tmp的祖先结点是否需要调整,并检查调整结束后,删除结点tmp。假设当前检查的结点为C(初始时为tmp),并设其父结点为B,兄弟结点为S,分为以下情况
(1)如果C为红色,将其变为黑色,结束
(2)如果C为黑色,且S为红色,则将S设置为黑色,将B设置为红色,分为以下情况:
(2.1)C为左孩子,则对B和S进行RR旋转,结束
(2.2)C为右孩子,则对B和S进行LL旋转,结束
(3)如果C和S均为黑色,则分为以下情况
(3.1)C为左孩子,且S的右孩子
S
r
S_r
Sr为红色,则将S设置为B的颜色,将B和
S
r
S_r
Sr都设置为黑色,然后对B和S进行RR旋转,结束
(3.2)C为左孩子,且S的左孩子
S
l
S_l
Sl为红色,则将
S
l
S_l
Sl设置为B的颜色,将B和S都设置为黑色,然后对B,S,
S
l
S_l
Sl进行RR旋转,结束
(3.3)C为左孩子,且S的左右孩子都没有红色,则将S变为红色,将B作为当前结点C从第一步开始继续检查
(3.4)C为右孩子,且S的左孩子
S
l
S_l
Sl为红色,则将S设置为B的颜色,将B和
S
l
S_l
Sl都设置为黑色,然后对B和S进行LL旋转,结束
(3.5)C为右孩子,且S的右孩子
S
r
S_r
Sr为红色,则将
S
r
S_r
Sr设置为B的颜色,将B和S都设置为黑色,然后对B,S,
S
r
S_r
Sr进行LR旋转,结束
(3.6)C为右孩子,且S的左右孩子都没有红色,则将S变为红色,将B作为当前结点C从第一步开始继续检查
//从结点C出发,对红黑树t的结构和颜色进行调整
void check_color(rbTree& t, rbTree& C) {
rbTree rt;
if (C->c == RED) {//情形1
C->c = BLACK;
rt = C;
}
else {
if (C->pa == NULL) {
t = C;
return;
}
rbTree B = C->pa, S;
if (B->lc == C) {//C为左孩子
S = B->rc;//S为右孩子
if (S == NULL || S->c == BLACK) {//S为红色
if (S->rc && S->rc->c == RED)//情形(3.1)
set_color(S, B->c, B, BLACK, S->rc, BLACK), rt = RR(B);
else if (S->lc && S->lc->c == RED)//情形(3.2)
set_color(S->lc, B->c, B, BLACK, S, BLACK), rt = RL(B);
else//情形(3.3)
set_color(S, RED), check_color(t, B);
}
else {//情形2.1
set_color(B, RED, S, BLACK), rt = RR(B);
}
}
else {//C为右孩子
S = B->lc;//S为左孩子
if (S == NULL || S->c == BLACK) {
if (S->lc && S->lc->c == RED)
set_color(S, B->c, B, BLACK, S->lc, BLACK), rt = LL(B);
else if (S->rc && S->rc->c == RED)
set_color(S->rc, B->c, B, BLACK, S, BLACK), rt = LR(B);
else
set_color(S, RED), check_color(t, B);
}
else {
set_color(B, RED, S, BLACK), rt = RR(B);
}
}
}
if (rt->pa == NULL)t = rt;//更新红黑树的根结点
}
//删除红黑树t中键值为k的结点
bool rbt_delete(rbTree& t, int k) {
rbTree C, t1 = t, pa = NULL, t2;
while (t1) {
pa = t1;//记录当前结点的父结点
if (t1->key > k)t1 = t1->lc;//删除节点在左子树
else if (t1->key < k)t1 = t1->rc;//删除结点在右子树
else {//找到结点
if (t1->lc == NULL && t1->rc == NULL) {
if (t1->c == BLACK)check_color(t, t1);//删除结点为黑色结点
if (t1->pa != NULL)pa = t1->pa;
if (pa->lc == t1)pa->lc = NULL;
else pa->rc = NULL;
delete t1;
t1 = NULL;
}
else if (t1->rc == NULL) {
t1->key = t1->lc->key;
t1 = t1->lc, k = t1->key;
}
else {
t2 = t1->rc;
while (t2->lc != NULL)t2 = t2->lc;
t1->key = t2->key;
t1 = t2;
k = t1->key;
}
}
}
return true;
}