红黑树的定义和插入在红黑树的实现(一)中已经描述和实现了,下面说一下红黑树的删除。
红黑树的删除也包括两个步骤:
1.删除结点
2.调整树满足红黑树的定义
首先是删除一个结点,同样可以按二叉排序树的删除结点来删除。删除结点又分为4中情况:
(1)删除结点没有左孩子,有右孩子
(2)删除结点没有右孩子,有左孩子
(3)删除结点为叶子结点
(4)删除结点既有左孩子又有右孩子
对于情况(1),可以直接用右孩子代替删除的结点,并且由于删除结点有右孩子,所以删除结点必定为黑色,右孩子必定为红色(假设删除结点为红色,右孩子必定为黑色,这样是违反红黑树定义4的,所以删除结点必定为黑色,同理右孩子必定为红色),因此只要把右孩子着为黑色,红黑树依然没有违反定义。对于情况(2),与情况(1)相同,只是把右孩子换成左孩子。对于情况(3),可以直接删除结点,不过如果该删除的结点为黑色,删除后会违反定义4,所以必须进行调整。对于情况(4),可以转换为前三种情况,因为可以用删除结点在中序排序中的后继结点代替该删除结点,相当于删除的是后继结点,而后继结点属于前面三种情况的一种。代码如下:
1 rb_node_t *rb_erase_node(rb_node_t *node, rb_node_t **root) 2 { 3 rb_node_t *replace, *p; 4 5 if (NULL == node) 6 return NULL; 7 if (NULL == node->lchild) 8 { 9 /*左孩子为空,直接用右孩子代替node*/ 10 replace = node->rchild; 11 p = node->parent; 12 if (replace) 13 { 14 replace->parent = p; 15 replace->color = BLACK; 16 } 17 else if (BLACK == node->color) 18 rb_erase_fix_node(node, root);/*注意这里*/ 19 if (NULL == p) 20 *root = replace; 21 else if (node == p->lchild) 22 p->lchild = replace; 23 else 24 p->rchild = replace; 25 } 26 else if (NULL == node->rchild) 27 { 28 /*右孩子为空,且左孩子不为空*/ 29 replace = node->lchild; 30 p = node->parent; 31 replace->parent = p; 32 replace->color = BLACK; 33 rebalance = NULL; 34 if (NULL == p) 35 *root = replace; 36 else if (node == p->lchild) 37 p->lchild = replace; 38 else 39 p->rchild = replace; 40 } 41 else 42 { 43 /*左右孩子均不为空*/ 44 replace = rb_next(node); 45 key_copy(node, replace); 46 info_copy(node, replace); 47 node = replace; 48 /*左孩子为空*/ 49 replace = node->rchild; 50 p = node->parent; 51 if (replace) 52 { 53 replace->parent = p; 54 replace->color = BLACK; 55 } 56 else if (BLACK == node->color) 57 rb_erase_fix_node(node, root);/*注意这里*/ 58 if (node == p->lchild) 59 p->lchild = replace; 60 else 61 p->rchild = replace; 62 } 63 return node; 64 }
现在我们来看调整。删除结点后真正需要调整的是遇到情况3,想想删除一个结点会违反红黑树的哪些定义呢?很有很能是违反第4条定义,因为可能会删除一个颜色为黑色的结点。红黑树的调整总共包括8种情况,和插入调整情况一样,其实就是4种,另外四种都是对称的,这里就只讨论删除结点为父结点左孩子的情况,将删除结点作为调整结点。情况(1)调整结点的兄弟为红色,那么父结点必定为黑色,将父结点设置为红色,兄弟结点设置为黑色,左旋父结点,这样就转化为了情况2;情况(2)调整结点的兄弟为黑色。情况(2)中可以分成三种情况讨论,即情况(2-1)兄弟结点的左右孩子均为黑色,这种情况可以直接设置兄弟结点颜色为红色,将调整结点设置为父亲结点;情况(2-2)兄弟结点的左孩子为红色,右孩子为黑色,将兄弟结点设置为红色,兄弟结点的左孩子设置为黑色,右旋兄弟结点转换为情况(2-3);兄弟结点的右孩子为红色,将兄弟结点的右孩子设置为黑色,兄弟结点的颜色设置为父结点的颜色,父结点设置为黑色,左旋父结点,这样就已经调整好了。这里要注意的就是调整完之后要将删除结点彻底的从树中删除,调整时由于调整的需要,并没有把结点从树上删除(算法导论用了一个叫NIL的结点作为了替代,所以直接删除了结点,这是实现细节,对于理解整个的调整没有什么影响的);还有就是如果兄弟结点的孩子右不错字的情况,要特别小心,自己就是没有考虑全部的情况,所以总是段错误,后来调试发现是空指针异常了。
static void rb_erase_fix_node(rb_node_t *node, rb_node_t **root) { rb_node_t *p, *s, *sl, *sr; while (node != *root && node->color == BLACK) { p = node->parent; if (node == p->lchild) { /*node为左孩子*/ s = p->rchild;/*s必定不为空*/ if (RED == s->color) { /*情况1:node的兄弟为红色,设置p为红色,s为黑色,左旋p,转换为情况2 * P S * / \ / \ * n? s --> p SR * / \ / \ * SL SR n? SL * */ p->color = RED; s->color = BLACK; rb_rotate_left(p, s); if (NULL == s->parent) *root = s; } else { sl = s->lchild; sr = s->rchild; if (NULL == sl && NULL == sr || sl && BLACK == sl->color && sr && BLACK == sr->color) { /*情况2:node的兄弟为黑色,设置s设为红色,node设置为p * p? p? * / \ / \ * n? S --> n? s * / \ / \ * SL SR SL SR * */ s->color = RED; node = p; } else if ((NULL == sr || sr && BLACK == sr->color) && sl && RED == sl->color) { /*情况3:node的兄弟的右孩子为黑色,左孩子为红色,设置sl设为黑色,s设置为红色,右旋s * p? p? * / \ / \ * n? S --> n? SL * / \ \ * sl SR s * \ * SR * */ sl->color = BLACK; s->color = RED; rb_rotate_right(s, sl); if (NULL == sl->parent) *root = sl; } else { /*情况4:node的兄弟的右孩子为红色,设置sr设为黑色,s设置为p的颜色,p设置为黑色,左旋p * p? s? * / \ / \ * n? S --> P SR * \ / * sr n? * */ sr->color = BLACK; s->color = p->color; p->color = BLACK; rb_rotate_left(p, s); if (NULL == s->parent) *root = s; node = *root; } } } else { /*node为右孩子*/ s = p->lchild;/*s必定不为空*/ if (RED == s->color) { /*情况1:node的兄弟为红色,设置p为红色,s为黑色,右旋p,转换为情况2 * P S * / \ / \ * s n? --> p SR * / \ / \ * SL SR SR n? * */ p->color = RED; s->color = BLACK; rb_rotate_right(p, s); if (NULL == s->parent) *root = s; } else { sl = s->lchild; sr = s->rchild; if (NULL == sl && NULL == sr || sl && BLACK == sl->color && sr && BLACK == sr->color) { /*情况2:node的兄弟的两个孩子均为黑色,设置s设为红色,node设置为p * p? p? * / \ / \ * S n? --> s n? * / \ / \ * SL SR SL SR * */ s->color = RED; node = p; } else if ((NULL == sl || sl && BLACK == sl->color) && sr && RED == sr->color) { /*情况3:node的兄弟的左孩子为黑色,右孩子为红色,设置sr设为黑色,s设置为红色,左旋s * p? p? * / \ / \ * S n? --> SR n? * / \ / * SL sr s * / * SL * */ sr->color = BLACK; s->color = RED; rb_rotate_left(s, sr); if (NULL == sr->parent) *root = sr; } else { /*情况4:node的兄弟的左孩子为红色,设置sl设为黑色,s设置为p的颜色,p设置为黑色,右旋p * p? s? * / \ / \ * S n? --> SL P * / \ * sl n? * */ sl->color = BLACK; s->color = p->color; p->color = BLACK; rb_rotate_right(p, s); if (NULL == s->parent) *root = s; node = *root; } } } } node->color = BLACK; }