红黑树删除时的情况 由于红黑数是有序树,当删除红黑色的一个结点时,用它的后继结点(也可以是前驱结点)来代替它,然后去删除它的后继结点(或者前驱结点)。后继结点是右子树中值最小的结点(前驱结点是左子树中值最大的结点)。可以知道,后继结点一定没有左子树(前驱结点一定没有右子树),所以,当在红黑树中删除一个结点时,只会有以下两种情况: 1.删除的结点只有一个孩子 2.删除的是叶子结点
情况1 此时,要删除的结点(N)只有一个孩子(可以是左孩子(NL),也可以是右孩子(NR)),由红黑树的性质,可以知道,N一定是黑色,这个唯一的孩子一定是红色。直接将N删除,然后用N的唯一的孩子结点去代替N,再将N染黑既可。 情况2 此时,删除的结点(N)是叶子结点,如果N是红色,直接删除既可。 如果N是黑色,由红黑树的性质,它一定存在一个兄弟结点B,在删除N后,需要调节树,使其重新成为一个红黑树,在调节过程中,需要关注N的兄弟结点B的颜色。 情况如下: N为黑,N一定有兄弟结点B @ ——N为左孩子 ———— B红 —————— B是红的,此时N和B的父亲结点§一定是黑的,且B一定存在两个黑色的孩子结点。将B染黑、P染红,以P左旋,然后更新B(B=P->right),此时B为黑 ———— B黑 —————— B没有红色孩子结点 ———————— P红 —————————— 此时,将B染红、P染黑,即完成 ———————— P黑 —————————— 将B染红,使N=P,P=N->parent,此时N为黑,回到@的地方继续判断 —————— B的左孩子(BL)红 ———————— B染红、BL染黑,以B右旋,更新B(B=P->right) —————— B的右孩子(BR)红 ———————— B染成P的颜色、P染黑、BR染黑,以P左旋,此时完成 ——N为右孩子 ———— B红 —————— B是红的,此时N和B的父亲结点§一定是黑的,且B一定存在两个黑色的孩子结点。将B染黑、P染红,以P右旋,然后更新B(B=P->left),此时B为黑 ———— B黑 —————— B没有红色孩子结点 ———————— P红 —————————— 此时,将B染红、P染黑,即完成 ———————— P黑 —————————— 将B染红,使N=P,P=N->parent,此时N为黑,回到@的地方继续判断 —————— B的右孩子(BR)红 ———————— B染红、BR染黑,以B左旋,更新B(B=P->left) —————— B的左孩子(BL)红 ———————— B染成P的颜色、P染黑、BL染黑,以P右旋,此时完成
代码
//node路径上少了一个黑色节点 node是parent的左或者右,函数开始调用的时候node=NULL void rbtree_delete_repair(RBTree *ptree,RBNode *node,RBNode *parent){ while((node==NULL||node->col==BLACK)&&node!=*ptree){ //node为NULL是因为调用函数开始之前,node为黑色叶结点且已被删除;node不为NULL且为黑是在调整平衡的过程中产生的情况;如果node是根节点,那么就不需要调整了 if(node == parent->left){ //node节点在左边 RBNode *brother = parent->right; if(brother->col == RED){ //兄弟节点为红 brother->col = BLACK; parent->col = RED; left_rotation(ptree,parent); //旋转的代码在之前的博客中(红黑树的插入操作) } brother = parent->right; //更新兄弟节点 //兄弟节点为黑,兄弟节点不存在红色子节点 if(!((brother->left!=NULL&&brother->left->col==RED)||(brother->right!=NULL&&brother->right->col==RED))){ brother->col = RED; if(parent->col == RED){ //父亲为红 parent->col = BLACK; return; //完成了 } //父亲为黑 node = parent; parent = node->parent; continue; } //兄弟节点有红色子节点 brother = parent->right; if(brother->right == NULL || brother->right->col==BLACK){ //这里兄弟左孩子为红,右孩子不存在或为黑 brother->col = RED; brother->left->col = BLACK; right_rotation(ptree,brother); } brother = parent->right; //兄弟的右节点为红色 brother->col = parent->col; parent->col = BLACK; brother->right->col = BLACK; left_rotation(ptree,parent); }else{//node节点在右边 RBNode *brother = parent->left; if(brother->col == RED){ //兄弟红 brother->col = BLACK; parent->col = RED; right_rotation(ptree,parent); } brother = parent->left; //兄弟为黑色,且兄弟结点没有红色的子节点 if(!((brother->left!=NULL&&brother->left->col==RED)||(brother->right!=NULL&&brother->right->col==RED))){ brother->col = RED; if(parent->col == RED){ //父亲红 parent->col = BLACK; return; } //父亲黑 node = parent; parent = node->parent; continue; } //兄弟有红色的右节点 brother = parent->left; if(brother->left==NULL || brother->left->col==BLACK){ brother->col = RED; brother->right->col = BLACK; left_rotation(ptree,brother); } brother = parent->left; //兄弟节点的左节点为红色 brother->col = parent->col; parent->col = BLACK; brother->left->col = BLACK; right_rotation(ptree,parent); break; } } (*ptree)->col = BLACK; //红黑树根节点必须是黑色 } //在查找到需要删除的节点node后,将其删除 void rbtree_delete_rbnode(RBTree *ptree,RBNode *node){ RBNode *left = node->left; RBNode *right = node->right; if(left == NULL && right == NULL){//叶子结点 if(node == *ptree){//删除的是根节点 *ptree = NULL; free(node); return; } //node为叶子节点 删除 RBNode *parent = node->parent; if(node == node->parent->left){ node->parent->left = NULL; }else{ node->parent->right = NULL; } char col = node->col; free(node); if(col == BLACK){//叶子节点是一个黑色节点,删除后需要平衡 rbtree_delete_repair(ptree,NULL,parent); } }else if(left != NULL && right != NULL){//左右都在 //转化成删除左子树最大节点 或者 右子树最小节点 while(right->left != NULL){ right = right->left; } node->data= right->data; rbtree_delete_rbnode(ptree,right); }else{//要删除的节点只有一个孩子 RBNode *tree = left!=NULL?left:right; tree->col = BLACK; if(node->parent == NULL){//删除的是根节点 *ptree = tree; }else{ if(node == node->parent->left){ node->parent->left = tree; }else{ node->parent->right = tree; } } free(node); } } //按值删除节点 int rbtree_delete(RBTree *ptree,T data,int (*compar)(T,T)){ RBTree *proot = ptree; while(*ptree != NULL){ int ret = compar(data,(*ptree)->data); if(ret == 0){ rbtree_delete_rbnode(proot,*ptree); return 0; }else if(ret < 0){ ptree = &(*ptree)->left; }else{ ptree = &(*ptree)->right; } } return -1; }