#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#include<stdio.h>
#include<stdlib.h>
#include<bits/stdc++.h>
using namespace std;
/*
红黑树的操作:
1.查找:同二叉排序树(后文称BST)的查找操作
2.插入:(1)找插入的位置:同BST的插入操作,先把节点插入到树中
(2)确定节点的初始颜色:
Ⅰ. 先假设为黑色,则一定破坏“黑路同”这一性质,
所以每次插入元素都需要调整,这与我们设计红黑树的理念相悖:放宽了对AVL树对于平衡因子的约束
因此不能为黑色。
Ⅱ. 所以节点只能是红色,尽管当插入的节点的父亲也是红色节点时会破坏“不红红”这一性质,但是并不是每次插入都需要调整
注:如果是在一颗空树上插入根节点,则根节点直接作为黑色
(3)如何调整?
Ⅰ. 染色:红变黑,黑变红
Ⅱ. 旋转:同AVL树的旋转,LL,LR,RL,RR
(4)分类讨论:
Ⅰ. 树为空,插入根节点,根节点赋为黑色
Ⅱ. 树不为空,插入节点x为红色,
如果x的父亲(p)为黑色,则无需调整;
如果x的父亲为红色(根据性质可以推导出爷爷一定是黑色),则需要调整:
再根据x的叔叔节点(u)颜色分类:
(1)叔叔节点为红色:将父亲(变黑),叔叔(变黑),爷爷(变红)节点均变色,然后将爷爷节点作为x不断处理,直到根节点(如果x为根节点,将其设置为黑色)
(2)叔叔节点为黑色:
Ⅰ. LL:将父节点设为黑色,爷爷节点设为红色,以爷爷节点为支点右旋
Ⅱ. LR:以父节点为支点左旋,转换成LL情况(左旋后父子关系发生改变,因此需要交换两个指针(x和p))
Ⅲ. RR:将父节点设为黑色,爷爷节点设为红色,以爷爷节点为支点左旋
Ⅳ. RL:以父节点为支点右旋,转换成RR情况(右旋后父子关系发生改变,因此需要交换两个指针(x和p))
口诀:是根为黑,非根为红
破坏“不红红”,要看叔节点
红叔长辈染,爷爷变新点
黑叔要旋转,代码要统一,两遍变一遍,还是父爷染
3.删除:(1)按照BST的删除操作(删除x):
Ⅰ. x的度为2,用中序遍历x的前驱或者后继节点y的数据替换x,问题转化为删除度为y节点
以y为x的中序遍历前驱节点为例,y为x左子树中最靠右的节点,因此y一定没有右孩子,换句话说y的度一定是0或1
因此可以转化为Ⅱ的情况
Ⅱ. x的度为1或者0,直接用x唯一的孩子y顶替x的位置(度为零的孩子为空节点)
(2)调整:关注x和y的颜色(假设y是父亲,x是y唯一的一个孩子)
按照x和y的颜色进行分类讨论:
Ⅰ. y红x黑:此时不破坏任何性质,不需要调整
Ⅱ. y黑x红:可能破坏“不红红”(需要看y的父亲),一定破坏“黑路同”(黑色节点少了一个)
此时直接将x变色(赋值为黑色)即可
补充概念:
双黑节点:删除一个度为0或1的黑色节点之后,其黑色的孩子顶替了y的位置,顶替之后我们将x对黑高的贡献为2
此时Ⅲ这种情况我们只需要思考如何将双黑节点变成普通黑色节点即可。
注:双黑节点只是我们为了方便理解,实际红黑树中并不存在这种节点
Ⅲ. y黑x黑:调用Ⅱ的代码后x替换了y的位置,接下来根据x是p的左孩子还是右孩子进行分类讨论。
假设p为x的父节点,w为x的兄弟节点(即x顶替y之后,p有两个孩子分别是x和w)
(1)x是右,w是左(L):再根据w与w的孩子的颜色进行分类。
(a)w是黑色,w的孩子都为黑色:x褪一层黑色(逻辑上的操作,实际代码没有体现)w变色(变红),
x和w褪掉的黑色交给p,如果p原本是红色 ,则p变色(变黑),结束;
否则p变成双黑节点,作为新的x,直到x的父亲y为红色或者不存在(根节点)
(注:到根节点才停止会导致整棵树的黑高-1)
(b)w是黑色,w的孩子至少有一个红色(下称r)的:
(注:w如果两个孩子都是红色,则作为b1和b2哪种情况都行,这里默认将左孩子看成红色,视为b1的情况)
(b1)w的左孩子是红色(LL):先以p为中心右旋,然后将r变色(变黑),【再将w变成p的颜色,最后将p赋成黑色】
(变色:r(w->r)变w,w变p,p变黑,然后以p为中心右旋)
注:正常需要根据p的颜色进行分类讨论,【】部分为p是红色时多进行的操作,
但是我们发现当p为黑色的时候加上这两步也不会出错,因此我们直接统一这两种情况,只写一套代码
(b2)w的右孩子是红色(LR):先将w和r变色,再以w为中心左旋,旋转之后将w作为p的左孩子(w = p->l),转化成LL的情况
(c)w是红色,w的孩子一定为黑色:w变色(变黑),p变色(变红),以p为中心右旋,旋转之后将w作为p的左孩子(w = p->l)
转化成了(a)或者(b)
(2)x是左,w是右(R):和(1)是对称的,左变右,右变左
*/
#define RED 0 //红色
#define BLACK 1 //黑色
typedef struct RBTreeNode
{
int data;//数据
struct RBTreeNode* l;//左孩子
struct RBTreeNode* r;//右孩子
int color;//颜色
struct RBTreeNode* parent;//父亲节点
}RBTNode, *RBTree;
//创建一个新节点
RBTNode* creatNode(int key, RBTNode* parent, RBTNode* l, RBTNode* r)
{
RBTNode* s = (RBTNode*)malloc(sizeof(RBTNode));
if (s != NULL)
{
s->data = key;
s->parent = parent;
s->l = l;
s->r = r;
s->color = RED;
}
return s;
}
//以红黑树的x节点为中心进行左旋(这里不仅需要维护左右孩子指针,还需要维护父亲指针)
RBTree left_rotate(RBTree root, RBTNode* x)
{
//y指向x的右孩子
RBTNode* y = x->r;
//y的左孩子变成x的右孩子(处理y和x的孩子的关系)
if (y != NULL)
{
x->r = y->l;
if (y->l != NULL)
{
y->l->parent = x;
}
//y的父亲变成x的父亲(处理y和x的父亲的关系)
y->parent = x->parent;
if (x->parent == NULL)//如果x是根节点,旋转后需要更新root的指向
{
root = y;
}
else//如果x不是根节点,则需要判断x是x->parent的左孩子还是右孩子
{
if (x == x->parent->l)
{
x->parent->l = y;
}
else
{
x->parent->r = y;
}
}
//y的左孩子是x,x的父亲是y(处理y和x的关系)
y->l = x;
x->parent = y;
}
return root;
}
//以红黑树的x节点为中心进行右旋(这里不仅需要维护左右孩子指针,还需要维护父亲指针)
RBTree right_rotate(RBTree root, RBTNode* x)
{
//y指向x的左孩子
RBTNode* y = x->l;
//y的右孩子变成x的左孩子(处理y和x的孩子的关系)
if (y != NULL)
{
x->l = y->r;
if (y->r != NULL)
{
y->r->parent = x;
}
//y的父亲变成x的父亲(处理y和x的父亲的关系)
y->parent = x->parent;
if (x->parent == NULL)//如果x是根节点,旋转后需要更新root的指向
{
root = y;
}
else//如果x不是根节点,则需要判断x是x->parent的左孩子还是右孩子
{
if (x == x->parent->l)
{
x->parent->l = y;
}
else
{
x->parent->r = y;
}
}
//y的右孩子是x,x的父亲是y(处理y和x的关系)
y->r = x;
x->parent = y;
}
return root;
}
//变色函数
void change_color(RBTNode* s)
{
s->color = (s->color == RED ? BLACK : RED);
}
//调整函数
RBTree fixup(RBTree root, RBTNode* s)
{
RBTNode* p = s->parent;//s的父亲
RBTNode* g = NULL;//s的爷爷
RBTNode* u = NULL;//s的叔叔
//如果父亲节点存在并且父亲是红色,则需要调整
while (p && p->color == RED)
{
g = p->parent;
if (p == g->r)//父亲是爷爷的右孩子
{
u = g->l;//则叔叔是爷爷的左孩子
//如果叔叔是红色
if (u != NULL && u->color == RED)
{
//红叔长辈染,爷爷变新点
change_color(g);
change_color(p);
change_color(u);
s = g;
p = s->parent;
}
else//运行到这里无论叔叔存不存在,都是黑色
{
//由于RL比RR多了一步,因此先判断是否是RL,然后统一执行RR操作
if (s == p->l)
{
//先右旋,然后交换s和p两个指针
root = right_rotate(root, p);
swap(s, p);
}
//如果是RR,则直接执行这里
//黑叔要旋转,代码要统一,两遍变一遍,还是父爷染
change_color(g);
change_color(p);
root = left_rotate(root, g);
}
}
else//父亲是爷爷的左孩子
{
u = g->r; //则叔叔是爷爷的右孩子
//如果叔叔是红色
if (u != NULL && u->color == RED)
{
//红叔长辈染,爷爷变新点
change_color(g);
change_color(p);
change_color(u);
s = g;
p = s->parent;
}
else//运行到这里无论叔叔存不存在,都是黑色
{
//由于LR比LL多了一步,因此先判断是否是LR,然后统一执行LL操作
if (s == p->r)
{
root = left_rotate(root, p);
swap(s, p);
}
//如果是LL,则直接执行这里
//黑叔要旋转,代码要统一,两遍变一遍,还是父爷染
change_color(g);
change_color(p);
root = right_rotate(root, g);
}
}
}
//循环调整之后记得将根节点恢复成黑色
root->color = BLACK;
return root;
}
//插入节点
RBTree insertRBT(RBTree root, int key)
{
//如果树为空
if (root == NULL)
{
root = creatNode(key, NULL, NULL, NULL);
root->color = BLACK;//根节点为黑色
return root;
}
//如果树不为空->执行BST插入操作
RBTNode* x = root;//记录插入位置
RBTNode* xp = NULL;//记录插入位置的父亲节点
//寻找插入位置
while (x != NULL)
{
xp = x;
if (key < x->data)
{
x = x->l;
}
else
{
x = x->r;
}
}
RBTNode* s = creatNode(key, xp, NULL, NULL);
//更新插入节点父亲的孩子信息
if (key < xp->data)
{
xp->l = s;
}
else
{
xp->r = s;
}
root = fixup(root, s);
return root;
}
//验证是否是红黑树(根据中序遍历+颜色进行判断)
void inorder_RBT(RBTNode* root)
{
if (root != NULL)
{
inorder_RBT(root->l);
cout << root->data << " " << root->color << " ";
if (root->parent != NULL)
{
cout << root->parent->data << endl;
}
else
{
cout << "root" << endl;
}
inorder_RBT(root->r);
}
}
//查找值为key的节点
RBTree findBST(RBTree root, int key)
{
if (root == NULL || root->data == key)
{
return root;
}
if (root->data < key)
{
return findBST(root->r, key);
}
else //(root->data > key)
{
return findBST(root->l, key);
}
}
//删除以root为根的红黑树删除元素值为key所在的节点k
//删除节点k后的调整函数
RBTree delete_fixup(RBTree root, RBTNode* x, RBTNode* p)//x为p的孩子
{
RBTNode* w = NULL;//x的兄弟节点w
//删除k后x为双黑节点(Ⅲ)
while (x == NULL
|| (x != NULL && x->color == BLACK)
&& x != root)
{
//Ⅲ.(1)//x右,w左
if (x == p->r)
{
w = p->l;
//(c)w红色,x黑色
if (w != NULL && w->color == RED)
{
//兄父变色,朝双黑x旋转
change_color(w);
change_color(p);
root = right_rotate(root, p);
w = p->l;
}//此时w已经变成黑色,转化成了(a)或者(b)
//(a)w黑色,w孩子也都是黑色
if ((w->l == NULL || (w->l != NULL && w->l->color == BLACK))//左孩子为黑色
&& (w->r == NULL || (w->r != NULL && w->r->color == BLACK))//右孩子为黑色
)
{
//兄弟变色,双黑上移
change_color(w);
x = p;
p = x->parent;
}
else//(b)w黑色,w孩子至少有一个为红色
{
//LR(w是p的左孩子,w的右孩子为红色)
//根据左孩子为黑进行判断,这样w两个孩子都为红色这种情况被归为LL型
if (w->l == NULL
|| (w->l != NULL && w->l->color == BLACK)
)
{
//先将w和r(也就是w->r)变色,再以w为中心左旋,旋转之后将w作为p的左孩子(w = p->l)
change_color(w);
change_color(w->r);
root = left_rotate(root, w);
w = p->l;
}
else//LL(w左孩子为红色或者两个孩子都是红色)
{
//变色规则:r(w->l)变w,w变p,p变黑,然后以p为中心右旋
w->l->color = w->color;
w->color = p->color;
p->color = BLACK;
right_rotate(root, p);
}
}
}
else//Ⅲ.(2)//x左,w右
{
w = p->r;
//(c)w红色,x黑色
if (w != NULL && w->color == RED)
{
//兄父变色,朝双黑x旋转
change_color(w);
change_color(p);
root = left_rotate(root, p);
w = p->r;
}//此时w已经变成黑色,转化成了(a)或者(b)
//(a)w黑色,w孩子也都是黑色
if ((w->l == NULL || (w->l != NULL && w->l->color == BLACK))//左孩子为黑色
&& (w->r == NULL || (w->r != NULL && w->r->color == BLACK))//右孩子为黑色
)
{
//兄弟变色,双黑上移
change_color(w);
x = p;
p = x->parent;
}
else//(b)w黑色,w孩子至少有一个为红色
{
//RL(w是p的右孩子,w的左孩子为红色)
//根据右孩子为黑进行判断,这样w两个孩子都为红色这种情况被归为RR型
if (w->r == NULL
|| (w->r != NULL && w->r->color == BLACK)
)
{
//先将w和r(也就是w->l)变色,再以w为中心左旋,旋转之后将w作为p的右孩子(w = p->r)
change_color(w);
change_color(w->l);
root = right_rotate(root, w);
w = p->r;
}
else//RR(w右孩子为红色或者两个孩子都是红色)
{
//变色规则:r(w->r)变w,w变p,p变黑,然后以p为中心左旋
w->r->color = w->color;
w->color = p->color;
p->color = BLACK;
left_rotate(root, p);
}
}
}
}
//Ⅱ.
if (x != NULL && x->color == RED)
{
change_color(x);
}
return root;
}
//删除k节点
RBTree deleteRBT(RBTree root, RBTNode* k)
{
//度为2的节点
if (k->l != NULL && k->r != NULL)
{
//找中序遍历前驱或者后继节点来替换(这里以后继为例)
RBTNode* replace = k->r;
while (replace->l)
{
replace = replace->l;
}
k->data = replace->data;
k = replace;//转化成了度为1或者0的节点
}
//度为1或者0的节点
RBTNode* p = k->parent;
RBTNode* child = NULL;
//child取代k的位置
if (k->l != NULL)//k的左孩子存在
{
child = k->l;
}
else//k的右孩子存在或者k没有孩子
{
child = k->r;
}
if (p != NULL)//k的父亲存在,child替换k的位置
{
if (k == p->l)
{
p->l = child;
}
else
{
p->r = child;
}
}
else//k的父亲不存在(k为根节点),child变成新的根节点
{
root = child;
}
if (child != NULL)//如果child不是空节点,则建立child和p的关系
{
child->parent = p;
}
int color = k->color;
free(k);
k = NULL;
if (color == BLACK)//只有k为黑色的时候才需要调整
{
root = delete_fixup(root, child, p);
}
return root;
}
RBTree delete_x(RBTree root, int key)
{
RBTNode* k = findBST(root, key);//先找到待删除的节点
if (k != NULL)//只有红黑树中存在该节点才进行删除
{
root = deleteRBT(root, k);
}
return root;
}
int main()
{
int n;
int a[105];
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
RBTree root = NULL;
for (int i = 1; i <= n; i++)
{
root = insertRBT(root, a[i]);
}
inorder_RBT(root);
//删除元素值为x的节点
int x;
cin >> x;
root = delete_x(root, x);
inorder_RBT(root);
return 0;
}
/*
样例输入:
9
10 40 30 60 90 70 20 50 80
60
正确输出:
10 1 30
20 0 10
30 1 root
40 1 60
50 0 40
60 0 30
70 0 80
80 1 60
90 0 80
10 1 30
20 0 10
30 1 root
40 1 70
50 0 40
70 0 30
80 1 70
90 0 80
*/
数据结构——红黑树代码
于 2024-06-01 11:01:31 首次发布