文章目录
红黑树
定义
-
大致平衡的二叉排序树 不绝对平衡
-
最长路径不会超过最短路径的两倍
-
放松了对平衡的限制,能保证插入、删除、查找效率维持在O(logn)
-
五个特征
-
每一个节点要么黑色,要么红色 需要额外的属性来记录节点的颜色
-
根结点是黑色的 根结点不能是红色
-
在一条路径上,红色的节点不能不能连续 如果一个节点为红色,那么父节点和子节点都不能是红色, 父节点必须是黑的,子节点也必须是黑的
-
所有的叶子节点(是为NULL的节点)都是黑色的
-
从任意一个节点出发到其所有的叶子节点路径上的黑色节点的数量必须相等
-
-
红黑树插入之后修复
-
每次插入的节点都是红色的 只会破坏红红规则(不允许连续的红色)
-
为什么不是插入黑色节点,是因为从根节点出发,到叶子节点如果有m条路径,插入之后,在其他某一条路径上增加一个黑色节点,剩下m-1条路径无法其他都增加一个黑色节点
-
红黑树插入 如果需要进行修复 必然是红红
-
变色
- 父节点和叔叔节点为红色的 把父节点和叔叔节点染黑 爷爷染红
- 对爷爷节点继需向上讨论
-
旋转
- LL 父在爷爷左边 当前节点在父亲左边
- 对爷爷进行右旋 把原来的父节点染黑 原来的爷爷染红 结束
- RR 父在爷爷右边 当前节点在父亲右边
- 对爷爷进行左旋 把原来的父节点染黑 原来的爷爷染红 结束
- LR 父在爷爷左边 当前节点在父亲右边
- 先对父节点左旋,然后对爷爷节点进行右旋 爷爷染红 把当前节点染黑
- RL 父在爷爷右边 当前节点在父亲左边
- 先对父节点右旋,然后对爷爷节点进行左旋 爷爷染红 把当前节点染黑
- LL 父在爷爷左边 当前节点在父亲左边
-
插入修复最多需要2次旋转和logn次颜色变换
-
红黑树删除
-
删除的节点没有子节点
- 被删除的节点为红色 直接删除
- 被删除的节点为黑色 进一步考虑
-
删除的节点只有一个子节点
- 它的子节点一定是红色的 用子节点替换要删除的节点,并且把子节点置黑
-
删除的节点有两个子节点
-
找左子树最大值替换当前节点的元素,去左子树中删除最大的节点
-
转换成上面两种情况
-
红黑树删除之后修复
只有在删除黑色节点时才需要修复
- 兄弟节点为红色
- 要删除的节点在左边
- 把父亲节点变为红色 兄弟节点变为黑色 左旋(parent)(将兄弟节点的左儿子变为node节点的兄弟节点),然后当成兄弟节点为黑色的情况来处理
- 要删除的节点在右边
- 把父亲节点变为红色 兄弟节点变为黑色 右旋(parent)(将兄弟节点的右儿子变为node节点的兄弟节点),然后当成兄弟节点为黑色的情况来处理
- 要删除的节点在左边
- 兄弟节点为黑色
-
兄弟节点的子节点都为黑色
-
父节点为红色
- 改变 父节点颜色为黑色,兄弟节点(brother)颜色为红色,平衡结束
-
父节点为黑色
- 改变 兄弟节点颜色为红色,然后把父节点当成被删除的节点,继续向上递归
-
-
兄弟节点的子节点不全为黑色(一黑一红)
-
要删除的节点在左边
-
右侄子(兄弟节点的右儿子)为黑色
- 把左侄子改为黑色 兄弟节点改为红色 然后对兄弟节点右旋(brother),兄弟节点变为了右侄子(红色),然后当成右侄子为红色的情况继续
-
右侄子(兄弟节点的右儿子)为红色
- 把右侄子改为黑色 兄弟节点的颜色改为和父节点一样 父节点改为黑色 然后左旋(parent),平衡结束
-
-
要删除的节点在右边
-
左侄子(兄弟节点的左儿子)为黑色
- 把右侄子改为黑色 兄弟节点改为红色 然后对兄弟节点左旋(brother),兄弟节点变为了左侄子(红色),然后当成左侄子为红色的情况继续
-
左侄子(兄弟节点的左儿子)为红色
- 把左侄子改为黑色 兄弟节点的颜色改为和父节点一样 父节点改为黑色 然后右旋(parent),平衡结束
-
-
-
红黑树实现
结构体定义
#define SUCCESS 0
#define FAILURE -1
typedef int ElemType;
enum COLOR {
RED = 0,
BLACK
};
struct RBNode {
ElemType elem;
struct RBNode *lchild;
struct RBNode *rchild;
struct RBNode *parent;
int color;
};
typedef struct RBNode* RBTree;
主要功能声明
// 插入
int insert_rbtree(RBTree *ptree, ElemType elem);
// 删除元素
int remove_rbtree(RBTree *ptree, ElemType elem);
主要操作
// 右旋
void right_rotation(struct RBNode *node, RBTree *ptree);
// 左旋
void left_rotation(struct RBNode *node, RBTree *ptree);
// 插入修复
static void insert_repaire(struct RBNode *node, RBTree *ptree);
// 删除修复
void remove_repair(struct RBNode *node, struct RBNode *parent, RBTree *ptree);
// 删除节点
static void remove_node(struct RBNode **pnode, RBTree *ptree);
主要功能实现
插入 insert_rbtree
int insert_rbtree(RBTree *ptree, ElemType elem)
{
assert(ptree != NULL);
struct RBNode **proot = ptree;
struct RBNode *parent = NULL; // 记录插入节点的父节点
while (*ptree != NULL) {
parent = *ptree;
if (elem < (*ptree)->elem) {
ptree = &(*ptree)->lchild;
} else if ((*ptree)->elem < elem) {
ptree = &(*ptree)->rchild;
} else {
return FAILURE;
}
}
struct RBNode *node = (struct RBNode*)malloc(sizeof(struct RBNode));
if (node == NULL) {
return FAILURE;
}
node->elem = elem;
node->lchild = node->rchild = NULL;
node->color = RED; // 插入的节点为红色
node->parent = parent;
*ptree = node;
insert_repaire(node, proot); // 插入修复
return SUCCESS;
}
删除 remove_rbtree
int remove_rbtree(RBTree *ptree, ElemType elem)
{
assert(ptree != NULL);
RBTree *proot = ptree;
while (*ptree != NULL) {
if (elem < (*ptree)->elem) {
ptree = &(*ptree)->lchild;
} else if ((*ptree)->elem < elem) {
ptree = &(*ptree)->rchild;
} else {
remove_node(ptree, proot); // 删除*ptree这个节点
return SUCCESS;
}
}
return FAILURE;
}
主要操作实现
右旋 right_rotation
/*
/ /
node left
/ \ 右旋 / \
left rchild -----------> ll node
/ \ / \
ll rr rr rchild
*/
// 右旋
void right_rotation(struct RBNode *node, RBTree *ptree)
{
struct RBNode *left = node->lchild;
left->parent = node->parent; // node的父亲变成left的父亲
if (node->parent != NULL) {
if (node == node->parent->lchild) { // node是左孩子, left也应该作为左孩子
node->parent->lchild = left;
} else {
node->parent->rchild = left;
}
} else { // node 是根节点 旋转之后left作为根节点
*ptree = left;
}
node->lchild = left->rchild;
if (left->rchild != NULL) {
left->rchild->parent = node;
}
left->rchild = node;
node->parent = left;
}
左旋 left_rotation
/*
/ /
node right
/ \ 左旋 / \
lchild right -----------> node rr
/ \ / \
rl rr lchild rl
*/
// 左旋
void left_rotation(struct RBNode *node, RBTree *ptree)
{
struct RBNode *right = node->rchild;
right->parent = node->parent;
if (node->parent != NULL) {
if (node == node->parent->rchild) {
node->parent->rchild = right;
} else {
node->parent->lchild = right;
}
} else {
*ptree = right;
}
node->rchild = right->lchild;
if (right->lchild != NULL) {
right->lchild->parent = node;
}
right->lchild = node;
node->parent = right;
}
插入修复 insert_repaire
static void insert_repaire(struct RBNode *node, RBTree *ptree)
{
struct RBNode *parent = node->parent;
while (IS_RED(node) && IS_RED(parent)) { // 红红 两个连续的红色 肯定有爷爷
struct RBNode *gp = parent->parent;
struct RBNode *uncle = (parent == gp->lchild) ? gp->rchild : gp->lchild; // 判断父亲节点在哪一边
if (IS_RED(uncle)) {
parent->color = BLACK; // 叔叔和父亲节点改为黑色,爷爷节点改为红色,要继续讨论
uncle->color = BLACK;
gp->color = RED;
node = gp;
parent = node->parent;
continue;
}
if (parent == gp->lchild) { // 父为左 叔叔为右 LR
if (node == parent->rchild) {
// 对父节点进行左旋
left_rotation(parent, ptree);
parent = node; // 当前节点变为父亲节点 原父亲节点变为左儿子
node = parent->lchild;
}
right_rotation(gp, ptree); // LL
} else { // 父为右 叔叔为左
if (node == parent->lchild) { // RL
right_rotation(parent, ptree);
parent = node;
node = parent->rchild;
}
left_rotation(gp, ptree); // RR
}
parent->color = BLACK; // 把原来的父节点染黑 原来的爷爷染红 结束
gp->color = RED;
break;
}
if (node->parent == NULL) { // node为根节点 根节点必须为黑 只有变色会进入
node->color = BLACK;
return;
}
}
删除修复 remove_repair
/*
node是被删除的节点 传过来的NULL 父节点为parent *ptree为根节点
*/
void remove_repair(struct RBNode *node, struct RBNode *parent, RBTree *ptree)
{
while (parent != NULL && IS_BLACK(node)) {
struct RBNode *brother;
if (node == parent->lchild) { // 自己在左
brother = parent->rchild;
if (IS_RED(brother)) { // 兄弟红 父黑 兄弟孩子黑
left_rotation(parent, ptree); // 左旋
parent->color = RED; // 把父亲节点变为红色
brother->color = BLACK; // 兄弟节点变为黑色
brother = parent->rchild; // 原来的侄子变成了兄弟
}
// 兄弟黑
// 兄弟孩子都为黑
if (IS_BLACK(brother) && IS_BLACK(brother->lchild) && IS_BLACK(brother->rchild)) {
if (IS_RED(parent)) { // 父为红
brother->color = RED; // 兄弟节点(brother)颜色为红色
parent->color = BLACK; // 父节点颜色为黑色
break; // 平衡结束
}
// 父为黑
brother->color = RED; // 兄弟节点颜色为红色
node = parent; // 然后把父节点当成被删除的节点
parent = node->parent;
continue; // 继续向上递归
}
// 兄弟孩子不全为黑
if (IS_BLACK(brother->rchild)) { // 右侄子(兄弟节点的右儿子)为黑色
// 经过旋转让远端侄子为红
brother->color = RED; // 兄弟节点改为红色
brother->lchild->color = BLACK; // 把左侄子改为黑色
right_rotation(brother, ptree); // 然后对兄弟节点右旋(brother)
brother = brother->parent; // 兄弟节点变为了右侄子(红色)
}
brother->color = parent->color; // 兄弟节点的颜色改为和父节点一样
parent->color = BLACK; // 父节点改为黑色
brother->rchild->color = BLACK; // 把右侄子改为黑色
left_rotation(parent, ptree); // 左旋
break; // 平衡结束
} else { // 自己在右
brother = parent->lchild;
if (IS_RED(brother)) { // 兄弟红 父黑 兄弟孩子黑
right_rotation(parent, ptree);
parent->color = RED;
brother->color = BLACK;
brother = parent->lchild; // 原来的侄子变成了兄弟
}
// 兄弟黑
// 兄弟孩子都为黑
if (IS_BLACK(brother) && IS_BLACK(brother->lchild) && IS_BLACK(brother->rchild)) {
if (IS_RED(parent)) { // 父为红
brother->color = RED;
parent->color = BLACK;
break;
}
// 父为黑
brother->color = RED;
node = parent;
parent = node->parent;
continue;
}
// 兄弟孩子不全为黑
if (IS_BLACK(brother->lchild)) {
// 经过旋转让远端侄子为红
brother->color = RED;
brother->rchild->color = BLACK;
left_rotation(brother, ptree);
brother = brother->parent;
}
brother->color = parent->color;
parent->color = BLACK;
brother->lchild->color = BLACK;
right_rotation(parent, ptree);
break;
}
}
}
删除节点 remove_node
// 把*pnode从*ptree这个红黑树中删除
static void remove_node(struct RBNode **pnode, RBTree *ptree)
{
struct RBNode *node = *pnode;
if (node->lchild != NULL && node->rchild != NULL) {
for (pnode = &(node->lchild); (*pnode)->rchild != NULL; pnode = &(*pnode)->rchild); // 找到最大值
node->elem = (*pnode)->elem;
node = *pnode; // 转换为删除*pnode节点
}
// *pnode所指向的节点 肯定是不会同时存在左右子树
struct RBNode *parent = node->parent;
if (node->lchild == NULL && node->rchild == NULL) { // 删除的是叶子节点
*pnode = NULL; // 指空
if (IS_BLACK(node)) {
remove_repair(NULL, parent, ptree); // 删除修复
}
} else { // 只有一个子节点 子节点一定是红色的
*pnode = node->lchild != NULL ? node->lchild : node->rchild;
(*pnode)->parent = parent; // 父亲节点也得赋值
(*pnode)->color = BLACK;
}
free(node);
}