红黑树:是一种自平衡二叉查询树,其查找性能在同类二叉树中比较高。
性质:
- 节点是红色或黑色。
- 根节点是黑色。
- 所有叶子都是黑色(叶子是NIL节点、空节点)。
- 每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
- 从任一节点到其每个叶子节点的所有简单路径都包含相同数目的黑色节点。
从红黑树的几个性质我们可以看出为何其查找性能比较高:由性质4与性质5决定了红黑树中最长路径的节点数不会超最短路径的两倍。这样的一种平衡树在其二分查找时速度非常快。当然这也就导致了红黑树的操作比较复杂,建议在学习了解红黑树之前我们需要对普通的二叉查询树有一定的了解。
下图为一颗红黑树:
ps:在红黑树中NULL节点看做是黑色的叶节点,也就是所有的叶子节点为黑色。
我们可以把红黑树看做一串葡萄,我们像摘葡萄一样从任何一点摘下子串,将“子串”根节点颜色变黑,则它都满足红黑树的性质。
红黑树操作
红黑树操作
先附上我实现红黑树的头文件代码(在此文章中末尾我会将完整的代码链接贴上以便交流):
/*
* rb_tree.h
*
* Created on: 2016年9月22号
* Author: caohonghui
*/
#ifndef RB_TREE_H_
#define RB_TREE_H_
typedef struct rb_tree_node_s rb_tree_node_t;
/*如果a > b 返回值大于0,如果a < b 返回值小于0,a = b 则返回0。*/
typedef int (*rb_node_compare_t)(rb_tree_node_t* a, rb_tree_node_t* b);
struct rb_tree_node_s
{
void *user_data;
struct rb_tree_node_s *left_child;
struct rb_tree_node_s *right_child;
struct rb_tree_node_s *parent_node;
int node_color; /* node color:1 (red),0 (black) */
};
typedef struct rb_tree_s
{
struct rb_tree_node_s *rb_node_root; /* root of the tree */
size_t node_size;
rb_node_compare_t rb_node_compare;
} rb_tree_t;
enum
{
GET_MAX, GET_MIN
};
int rb_tree_root_init(rb_tree_t *rb_tree, rb_node_compare_t rb_node_compare);
rb_tree_node_t *rb_tree_insert(rb_tree_t *rb_tree, rb_tree_node_t *node);
rb_tree_node_t *rb_tree_remove(rb_tree_t *rb_tree, rb_tree_node_t *node);
rb_tree_node_t *rb_tree_find(rb_tree_t *rb_tree, rb_tree_node_t *node);
rb_tree_node_t *rb_tree_nfind(rb_tree_t *rb_tree, rb_tree_node_t *node);
rb_tree_node_t *rb_tree_next(rb_tree_node_t *node);
rb_tree_node_t *rb_tree_prev(rb_tree_node_t *node);
rb_tree_node_t *rb_tree_min_or_max(rb_tree_t *rb_tree, int flag);
#define RB_MIN(rb_tree) rb_tree_min_or_max((rb_tree), GET_MIN)
#define RB_MAX(rb_tree) rb_tree_min_or_max((rb_tree), GET_MAX)
#define RB_FOREACH(x,rb_tree) \
for((x) = RB_MIN((rb_tree)); \
(x) != NULL; \
(x) = rb_tree_next(x))
#define RB_FOREACH_FROM(x,y) \
for((x) = (y); \
((x) != NULL) && ((y) = rb_tree_next(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_REVERSE(x,rb_tree) \
for((x) = RB_MAX((rb_tree)); \
(x) != NULL; \
(x) = rb_tree_prev(x))
#define RB_FOREACH_REVERSE_FROM(x,y) \
for((x) = (y); \
((x) != NULL && ((y) = rb_tree_prev(x), (x) != NULL)); \
(x) = (y))
#define RB_FOREACH_
#endif /* RB_TREE_H_ */
插入操作:
红黑树的插入操作和二叉树极为相似,但在其操作上加上了颜色调整。一颗红黑树插入新点时,我们比较其与根节点的值,小于则往左子树走,大于则往右子树走,找到其合适添加位置。下面我们看其操作伪代码:
RB-INSERT(T,z)
y <- NIL[T] //Step 1.将y设置为哨兵(可以当做NULL看待),将x设置为根
x <- root[T]
while x!=NIL[T] //Step 2.搜索,如果z比x小,向左子树搜索,否则向右子树搜索,y插入位置
do y <- x
if key[z] < key[x]
then x <- left[x]
else x <- right[x]
p[z] <- y //设置父节点
if y=NIL[T] //Step 3.若为空树,z为根,
then root[T] <- z
else if key[z] < key[y] //若z比y小,z放在y的左子树
then left[y] <- z
else right[y] <- z //否则,z放在y的右子树
left[z] <- NIL[T] //Step 4.将z左右子树设置为哨兵,颜色设置为红色
right[z] <- NIL[T]
color[z] <- RED
RB-INSERT-FIXUP(T,z) //Step 5.红黑树调整
插入代码实现:
/*二叉查找树性质:左孩子节点小于父节点,右孩子节点大于父节点*/
rb_tree_node_t *rb_tree_insert(rb_tree_t *rb_tree, rb_tree_node_t *node)
{
rb_tree_node_t *tmp = NULL;
rb_tree_node_t *parent = NULL;
int comp;
tmp = rb_tree->rb_node_root;
while (NULL != tmp)
{
parent = tmp;
comp = rb_tree->rb_node_compare(parent, node);
if (comp > 0)
tmp = tmp->left_child;
else if (comp < 0)
tmp = tmp->right_child;
else
return tmp;
}
RB_NODE_INIT(node);
node->parent_node = parent;
if (NULL != parent)
{
if (comp > 0)
parent->left_child = node;
else
parent->right_child = node;
}
else
{
rb_tree->rb_node_root = node;
}
/*新加节点置为红色,所以各结点路径长度没有变化,只需要调整颜色即可*/
_rb_tree_insert_color(rb_tree, node);
rb_tree->node_size++;
return NULL;
}
插入颜色调整
在进行颜色调整前我们必须了解两个概念:左旋转与右旋转,对操作红黑树进行颜色调整时会用到。下面还是看一张图片让我们心里先有一个了解,下图示例在节点5上进行右旋、在节点3上进行左旋。
右旋:
当在某个结点x上,做右旋操作时,我们假设它的左孩子y不是NULL,x可以为树内任意左孩子不是NULL的结点。
当在某个结点x上,做右旋操作时,我们假设它的左孩子y不是NULL,x可以为树内任意左孩子不是NULL的结点。
右旋以x到y之间的链为“支轴”进行,它使结点y成为该孩子树的根,y的右孩子结点为x,而y的原右孩子则成为x的左孩子。
static void _rb_tree_rotate_right(rb_tree_t *rb_tree, rb_tree_node_t *node)
{
rb_tree_node_t *l_child;
l_child = node->left_child;
if ((node->left_child = l_child->right_child) != NULL)
l_child->right_child->parent_node = node;
if ((l_child->parent_node = node->parent_node) != NULL)
{
if (node->parent_node->left_child == node)
node->parent_node->left_child = l_child;
else
node->parent_node->right_child = l_child;
}
else
rb_tree->rb_node_root = l_child;
l_child->right_child = node;
node->parent_node = l_child;
return;
}
当在某个结点x上,做左旋操作时,我们假设它的右孩子y不是NULL,x可以为树内任意右孩子不是NULL的结点。
左旋以x到y之间的链为“支轴”进行,它使结点y成为该孩子树的根,y的左孩子结点为x,而y的原左孩子则成为x的右孩子。
static void _rb_tree_rotate_left(rb_tree_t *rb_tree, rb_tree_node_t *node)
{
rb_tree_node_t *r_child;
r_child = node->right_child;
if ((node->right_child = r_child->left_child) != NULL)
r_child->left_child->parent_node = node;
if ((r_child->parent_node = node->parent_node) != NULL)
{
if (node->parent_node->left_child == node)
node->parent_node->left_child = r_child;
else
node->parent_node->right_child = r_child;
}
else
rb_tree->rb_node_root = r_child;
r_child->left_child = node;
node->parent_node = r_child;
return;
}
我们发现一个特性:左旋与右旋不会影响二叉查询树的性质。
插入新节点情况分析:
我们将插入的新节点颜色置红色,这样根节点到各叶节点的路径还是不变的,我们只需进行颜色调整,以便满足红黑树性质。
1.没有父节点:新节点也就是根节点,将其颜色变黑便满足红黑树性质要求。
2.父节点是黑色:无需进行颜色调整,已满足红黑树定义。
3.父节点是红色,此时违反性质4,相邻节点不能都为红色节点,需进行颜色调整:
因其父节点为红色,则其祖父节点必定为黑色,现需根据叔叔节点颜色进行分析调整。
当节点为nil(NULL)时我们得在脑子里面时刻把它看做是黑色叶节点,这样后面的情况分析才能顺利理解。
3.1:新插入节点的叔叔节点为红色节点(n:new节点;f:父节点;p:祖父节点;u:叔叔节点)
调整方法:父节点颜色为红色则祖父节点必为黑色节点,先将祖父节点颜色下放到左右子节点,然后祖节点颜色变成红色,这样各叶节点路径长度保存不变,我们将冲突节点上移置祖父节点p,下次以祖父节点进行调整(
这里不用考虑父节点的位置是左还是右)。转换后如下图
3.2:叔叔节点是黑色节点,且新节点及父节点都是左孩子节点:
调整方法:
首先将父f节点变为黑节点,祖父节点p变成红色节点,这样根节点到u路径中的黑色节点总数减一,这样路径长度就不平衡了。这时我们在p节点上进行右旋,让f节点成为子树的根节点,这样根节点到n分支及u分支的路径长度再次平衡,颜色冲突解决,树满足了红黑树定义要求。转换后如下图所示:
3
.3:叔叔节点是黑色节点,父节点是左孩子节点,但是新节点是右孩子节点
调整方法:
我们仔细看一看3.3情况是不是与3.2有点相像?只是新节点变成了右节点。如果我们可以将此种情况变成3.2一样岂不是就找到解决的出路了。我们在f节点上进行左旋使n节点成为子树的根节点,这样既没有影响根节点到各叶节点路径长度且将问题转换成与3.2一样的状况,如下图所示。
在这里我们得重新调整父节点f与n节点的指向。把f点当做新插入点n,这样就变成了前面3.2一样的状况了,按照3.2进行调整解决。
3.4: 叔叔节点是黑色节点,新节点f与父节点n都是右孩子节点
调整方法:此种情况与3.2类似,只是新插入节点与父节点变成了右孩子节点。我们先将祖父节点p颜色变红,父节点f颜色变黑,然后在节点p上进行左旋转,这样就解决了颜色冲突,满足了红黑树性质。转换后如下图所示:
3
.5:叔叔节点是黑色节点,父节点为右孩子节点,而新插入节点为左孩子节点
调整方法:与前面3.3调整方法类似,我们把它转为3.4一样的状况。先在f节点上进行右旋转,变成如下图所示:
然后调整n节点及f节点指向:n与f指向互换。这样就与3.4情况一样。按照3.4方法进行颜色调整以致满足红黑树定义。
插入颜色调整伪代码:
RB-INSERT-FIXUP(T,z)
while color[p[z]]=RED //插入节点父节点是红色
do if p[z]=left[p[p[z]]] //父节点为左孩子节点
then y <- right[p[p[z]]]
if color[y]=RED
then color[y] <- BLACK //情况3.1,z的叔叔y是红色
color[p[z]] <- BLACK
color[p[p[z]]] <- RED
z <- p[p[z]]
else
if z=right[p[z]] //情况3.3,z的叔叔y是黑色,且z是右孩子
then z <- p[z]
LEFT-ROTATE(T,z) //以父节点为中心节点左旋
color[p[z]] <- BLACK //情况3.2,z的叔叔y是黑色,且z是左孩子
color[p[p[z]]] <- RED
RIGHT-ROTATE(T,p[p[z]]) //以祖节点为中心右旋
else (same as then clause with “right” and “left” exchanged)
color[root[T]] <- BLACK
插入颜色调整代码:
/*插入节点后进行颜色调整*/
static void _rb_tree_insert_color(rb_tree_t *rb_tree, rb_tree_node_t *node)
{
rb_tree_node_t *parent, *grandpa, *uncle;
/*父节点若为黑色不存在颜色冲突,满足红黑树性质*/
while ((parent = node->parent_node) != NULL && parent->node_color != RB_BLACK)
{
grandpa = parent->parent_node;
if (grandpa->left_child == parent)
{
uncle = grandpa->right_child;
if (NULL != uncle && uncle->node_color == RB_RED)/*叔节点是红色,将冲突向上移*/
{
grandpa->node_color = RB_RED;
uncle->node_color = parent->node_color = RB_BLACK;
node = grandpa;
}
else
{
/*node是右孩子节点*/
if (node == parent->right_child)
{
/*以父节点为中心节点左旋转*/
_rb_tree_rotate_left(rb_tree, parent);
node = parent;
parent = node->parent_node;
}
grandpa->node_color = RB_RED;
parent->node_color = RB_BLACK;
/*以祖父节点为中心节点右旋转*/
_rb_tree_rotate_right(rb_tree, grandpa);
}
}
else
{
uncle = grandpa->left_child;
if (NULL != uncle && uncle->node_color == RB_RED)/*叔节点是红色,将冲突向上移*/
{
grandpa->node_color = RB_RED;
uncle->node_color = parent->node_color = RB_BLACK;
node = grandpa;
}
else
{
if (node == parent->left_child)
{
/*以父节点为中心节点右旋转*/
_rb_tree_rotate_right(rb_tree, parent);
node = parent;
parent = node->parent_node;
}
grandpa->node_color = RB_RED;
parent->node_color = RB_BLACK;
/*以祖父节点为中心节点左旋转*/
_rb_tree_rotate_left(rb_tree, grandpa);
}
}
}
rb_tree->rb_node_root->node_color = RB_BLACK;
return;
}
删除节点操作
删除操作思路与二叉查询树类似,先按二叉查询树删除方法删除节点,后进行颜色调整。分为三种情况:
1.删除节点z左右孩子节点为NULL:直接删除节点z。
2.删除节点z有左孩子或者右孩子节点:直接删除节点z,将父节点与x的左孩子或者右孩子节点关联起来。
3.删除节点z有左右孩子节点:为保证二叉查询树性质我们往往会取删除节点的左孩子节点的最右节点y替换z节点以达到删除节点z的目的,且保持了二叉查询树性质(
这里真正删除的节点为y节点,y节点可能为z的孩子节点在后面得特殊处理)。
删除节点伪代码:
RB-DELETE(T, z)
if left[z] = nil[T] or right[z] = nil[T] //没有或者有一个儿子
then y ← z //z赋值给y(真正删除的点是其本身)
else y ← TREE-SUCCESSOR(z) //有两个儿子,取左子树的最大节点或右子树的最小节点
if left[y] ≠ nil[T]
then x ← left[y]
else x ← right[y] //x为真正删除节点的可能非nil的孩子节点
p[x] ← p[y]
if p[y] = nil[T] //要删除的为根节点,则直接用x替代根节点(y的父节点为nil,则判断是根节点)
then root[T] ← x
else if y = left[p[y]] //要删除的节点在左子树,则x放在在左子树
then left[p[y]] ← x
else right[p[y]] ← x //要删除的节点在右子树,则x放在在右子树
if y ≠ z //z有两个儿子(没有儿子或者只有一个儿子则真正删除的节点是其本身)
then key[z] ← key[y] //将y的数据给z,实际上是删除的右子树的最小节点,然后把这个节点的数据拷到了z的位置
copy y's satellite data into z
//实际删除节点y的颜色为黑色则需进行颜色调整
//1,真正被删除的节点必定是只有一个红色孩子或没有孩子的结点。2,如果真正的删除点是一个红色结点,那么它必定是一个叶子结点。
if color[y] = BLACK
then RB-DELETE-FIXUP(T, x)
return y
删除节点代码:
/*
* 删除节点:node节点必须保证是rb_tree树中关联存在的点,否则出现未知错误
* */
rb_tree_node_t *rb_tree_remove(rb_tree_t *rb_tree, rb_tree_node_t *node)
{
rb_tree_node_t *child, *parent, *old;
int color;
old = node;
if (NULL == node->left_child)
child = node->right_child;
else if (NULL == node->right_child)
child = node->left_child;
else /*删除节点有两孩子节点*/
{
#if 0
/*方法一*/
rb_tree_node_t *tmp;
node = node->right_child;
while ((tmp = node->left_child) != NULL) /*取右孩子最左节点,可能就是右孩子*/
node = tmp;
/*将old与node节点交换,转为删除节点old只有一个孩子节点或者无孩子节点的情况*/
if (NULL == old->parent_node)
rb_tree->rb_node_root = node;
else if (old->parent_node->left_child == old)
old->parent_node->left_child = node;
else
old->parent_node->right_child = node;
old->left_child->parent_node = node;
old->right_child->parent_node = node;
/*node节点为old的孩子节点*/
if (old->right_child == node)
old->right_child = old;
if (node->parent_node->right_child == node)
node->parent_node->right_child = old;
else if (node->parent_node->left_child == node)
node->parent_node->left_child = old;
if (NULL != node->left_child)
node->left_child->parent_node = old;
if (NULL != node->right_child)
node->right_child->parent_node = old;
RB_SWAP_NODE(old, node);
return rb_tree_remove(rb_tree, old);
#else
/*方法二*/
/*取右孩子最左节点(可能就是右孩子本身),替换要移除的节点*/
rb_tree_node_t *tmp;
node = node->right_child;
while ((tmp = node->left_child) != NULL)
node = tmp; /*找到移动替换的节点,也就是真正删除的节点*/
child = node->right_child; /*node节点肯定没有左孩子,可能有右孩子*/
parent = node->parent_node;
color = node->node_color;
if (NULL != child)
child->parent_node = parent;
if (NULL != parent)
{
if (node == parent->right_child)
parent->right_child = child;
else
parent->left_child = child;
}
else
{ /*此处不会发生*/
rb_tree->rb_node_root = child;
}
/*如果真正删除的节点是old的孩子节点*/
if (node->parent_node == old)
parent = node;
/*node替换old节点*/
RB_ASSIGN_NODE_VALUE(node, old);
if (NULL != old->parent_node)
{
if (old->parent_node->left_child == old)
old->parent_node->left_child = node;
else
old->parent_node->right_child = node;
}
else
{
rb_tree->rb_node_root = node;
}
/* old有两孩子节点,我们是取右孩子的最左孩子替代old节点,
* 如果最左节点就是右孩子,则右孩子已经被删除,所以此处
* 要判断右孩子是否为空*/
old->left_child->parent_node = node;
if (NULL != old->right_child)
old->right_child->parent_node = node;
goto color;
#endif
}
parent = node->parent_node;
color = node->node_color;
/*直接删除node节点*/
if (NULL != child)
child->parent_node = parent;
if (NULL != parent)
{
if (parent->left_child == node)
parent->left_child = child;
else
parent->right_child = child;
}
else
{
rb_tree->rb_node_root = child;
}
color: if (color == RB_BLACK) /*删除节点为红色不影响红黑树性质*/
_rb_tree_move_color(rb_tree, parent, child);
rb_tree->node_size--;
RB_NODE_INIT(old);
return old;
}
删除节点颜色调整
情况分析:真正删除的节点必定只有一个孩子节点或者没有孩子节点,也就是说一个孩子节点为NULL或者两个孩子节点都为NULL。根据红黑树性质,每棵子树路径都一样长,所以当有一个孩子节点时,此孩子节点必为红色节点。
- 删除节点为黑色节点,有一个孩子节点不为NULL。
- 删除节点为黑色节点,两孩子都为NULL。
- 删除节点为红色节点,则其孩子节点都为NULL。
下面的图示分析过程中o(old)表示真正删除的节点,o节点会被其孩子节点n(new)所替代,通过前面1、2点分析得出n节点为NULL或者为红色节点。节点为绿色表示颜色未知。
1.删除节点o为红色节点:则左右孩子均为NULL,直接删除o节点,不影响红黑树性质。
2.删除节o点为黑色节点有一不为NULL的孩子节点:不为NULL孩子节点必定为红色节点。
调整我们方法:将n节点颜色变成黑色便就满足了红黑树性质。
3.删除节点o为黑色节点且孩子节点都为NULL,则n节点为NULL。删除节点o后根节点到n叶节点的路径减少了一个黑色节点,此时树不能满足红黑树性质则现需要根据删除节点o后n的兄弟节点及侄儿节点颜色来进行调整。
3.1 兄弟节点为红色
由于兄弟节点为红色,则可以推出父节点为黑色(n可以为左或右孩子节点):
调整方法:
当n为左孩子节点时,先把兄弟节点变黑,父节点变红,然后在父节点上做左旋转。此时我们发现路径还是保持原样没有平衡,根节点到n路径上少一黑节点。不过此时变成了黑兄红父的情况。我们以n点继续调整,转3.2调整。
同理n为右孩子节点。
3.2:兄弟节点为黑色
此时需要根据兄弟节点的孩子节点颜色及父节点颜色来进行调整。
3.2.1:黑兄、双黑侄儿、红父
调整方法:将父节点颜色变黑,兄弟节点颜色变红,此时便满足红黑树性质,完成调节。
3.2.2:黑兄、二黑儿子、黑父:
调整方法:现将兄弟节点变红色,将父节点左右至叶节点的路径调平,然后n指向父节点,也就是将冲突判断节点上移至父节点,以父节点重新进行分析判断调整。
3.2.3:黑兄、存在红侄儿节点(父节点颜色未知)
3.2.3.1:n节点为左孩子节点,且左侄儿是红色节点:
调整方法:将父节点的颜色给兄弟节点,父节点变黑,左侄儿变黑,然后在父节点上进行左旋转,调整完毕,树满足红黑树性质。
3.2.3.2:n节点为左孩子节点,且右侄儿是红色节点
调整方法:此时比3.2.3.1多一步调整,我们先将左侄儿染黑兄弟染红,在兄弟节点上进行右旋转这样就变成了3.2.3.1一样的状况(此处右旋以后将侄儿点摘下发现其两边路径一样,且满足红黑树性质),然后按照3.2.3.1方法进行调整,这里的调整n节点指向。
3.2.3.3:n节点为右孩子节点,左侄儿为红色节点
调整方法:类似3.2.3.1,
将父节点的颜色给兄弟节点,父节点变黑,右侄儿变黑,然后在父节点上进行右旋转,调整完毕,树满足红黑树性质。
3.2.3.4:n节点为右孩子节点,右侄儿为红色节点
调整方法:此时比3.2.3.3
多一步调整,我们先将右
侄儿染黑兄弟染红,在兄弟节点上进行左
旋转这样就变成了3.2.3.3
一样的状况,然后按照3.2.3.3
方法进行调整。
所有情况分析完毕,下面看删除颜色调整伪代码:
while x ≠ root[T] and color[x] = BLACK
do if x = left[p[x]] //x为左节点
then w ← right[p[x]] //兄弟节点,是右节点
//红兄的情况(黑父)
if color[w] = RED
then color[w] ← BLACK // Case 3.1 兄弟染黑
color[p[x]] ← RED // Case 3.1 父节点染红
LEFT-ROTATE(T, p[x]) // Case 3.1 以父节点进行左旋
w ← right[p[x]] // Case 3.1 指向新的兄弟节点(转变成了黑兄的情况[父红])
//黑兄的情况(父颜色未知)
if color[left[w]] = BLACK and color[right[w]] = BLACK //黑兄左右侄子是黑色(父节点颜色未知)
then color[w] ← RED //Case 3.2.1 兄弟节点染红
x ← p[x] //Case 3.2.1 以父节点进行再次调整(父节点是红色或者黑色)
else if color[right[w]] = BLACK //左侄子红色
then color[left[w]] ← BLACK //Case 3.2.3.2 左侄子染黑
color[w] ← RED //Case 3.2.3.2 兄弟染红
RIGHT-ROTATE(T, w) //Case 3.2.3.2 以兄弟节点右旋
w ← right[p[x]] //Case 3.2.3.2 新兄弟节点,其实为原左侄子节点(
//转成了右侄子是红色与3.2.3.1的情况一样)
color[w] ← color[p[x]] //Case 3.2.3.1 右侄子是红色:父节点颜色给兄弟节点
color[p[x]] ← BLACK //Case 3.2.3.1 父节点染黑
color[right[w]] ← BLACK //Case 3.2.3.1 右侄子染黑
LEFT-ROTATE(T, p[x]) //Case 3.2.3.1 父节点左旋
x ← root[T] //Case 3.2.3.1 调整完毕
else (same as then clause with "right" and "left" exchanged)
color[x] ← BLACK
删除节点调整代码:
/*删除节点后进行颜色调整*/
static void _rb_tree_move_color(rb_tree_t *rb_tree, rb_tree_node_t *parent, rb_tree_node_t *crr_node)
{
rb_tree_node_t *brother;
while (((NULL == crr_node) || RB_BLACK == crr_node->node_color) && rb_tree->rb_node_root != crr_node)
{
if (parent->left_child == crr_node)/*左孩子*/
{
brother = parent->right_child;
/*红兄:转为黑兄红父情况*/
if (RB_RED == brother->node_color)
{
brother->node_color = RB_BLACK;
parent->node_color = RB_RED;
_rb_tree_rotate_left(rb_tree, parent);
brother = parent->right_child;
}
/*黑兄双黑侄儿*/
if ((NULL == brother->left_child || RB_BLACK == brother->left_child->node_color)
&& (NULL == brother->right_child || RB_BLACK == brother->right_child->node_color))
{
brother->node_color = RB_RED;
crr_node = parent;
parent = crr_node->parent_node; /*将检查点向上移,以父节点进行调整*/
}
else
{
/*左侄儿为红色节点:转为右侄儿为红色节点的情况*/
if (NULL == brother->right_child || RB_BLACK == brother->right_child)
{
brother->left_child->node_color = RB_BLACK;
brother->node_color = RB_RED;
_rb_tree_rotate_right(rb_tree, brother);
brother = parent->right_child;
}
/*右侄儿为红色节点*/
brother->node_color = parent->node_color;
brother->right_child->node_color = RB_BLACK;
parent->node_color = RB_BLACK;
_rb_tree_rotate_left(rb_tree, parent);
crr_node = rb_tree->rb_node_root;
break;
}
}
else
{
brother = parent->left_child;
/*红兄:转为黑兄红父情况*/
if (RB_RED == brother->node_color)
{
brother->node_color = RB_BLACK;
parent->node_color = RB_RED;
_rb_tree_rotate_right(rb_tree, parent);
brother = parent->left_child;
}
/*黑兄双黑侄儿*/
if ((NULL == brother->left_child || RB_BLACK == brother->left_child->node_color)
&& (NULL == brother->right_child || RB_BLACK == brother->right_child->node_color))
{
brother->node_color = RB_RED;
crr_node = parent;
parent = crr_node->parent_node;/*将检查点向上移,以父节点进行调整*/
}
else
{
/*右侄儿为红色节点:转为左侄儿为红色节点的情况*/
if (NULL == brother->left_child || RB_BLACK == brother->left_child)
{
brother->right_child->node_color = RB_BLACK;
brother->node_color = RB_RED;
_rb_tree_rotate_left(rb_tree, brother);
brother = parent->left_child;
}
/*左侄儿为红色*/
brother->node_color = parent->node_color;
brother->left_child->node_color = RB_BLACK;
parent->node_color = RB_BLACK;
_rb_tree_rotate_right(rb_tree, parent);
crr_node = rb_tree->rb_node_root;
break;
}
}
}
if (crr_node)
crr_node->node_color = RB_BLACK;
return;
}
完整代码:
参考资料:
《算法导论》