红黑树(三)删除

来源:互联网 发布:北京特比尔软件 编辑:程序博客网 时间:2024/06/05 16:57

删除操作比插入复杂一些。首先我们先来了解一些红黑树的特性。这些是我随意列举的,供大家参考。

  1、红色节点的父亲黑孩子一定是黑色。(nil是黑节点)

  2、单支节点,只能是黑红。(红,黑黑,不符合规则4,到树尾黑节点个数相同)

  3、真正的删除节点一定是单支节点或者叶子节点。(没有孩子的节点)

接下来我们讲如何找真正的删除节点。

有左右子树的情况

如果8是删除节点,那么10就是真正的删除节点。

查找方法是,找8节点的右子树中的最小的节点,根据二叉树的性质,我们可以知道,8节点的右孩子的左孩子的左孩子……一直到left为nil的节点。就是10。(while ( y->left != nil )  y = y->left;)

单支节点或者没有叶子节点的情况

删除节点跟真正的删除节点是一个。

找到真正的删除节点后,我们把删除节点的值变成真正的删除节点的值。这时候把真正的删除节点从红黑树中剔除。

如果真正的删除节点是黑色,则破坏了红黑树的性质,进行调整。(比如删除8,真正的删除节点是10,10是黑色节点,所以需要调整红黑树)

复制代码
void rb_remove(const int key, Tree * tree){    Node * x, * y, * z;//x是删除点,y是真正的删除点,z是y的子节点    x = search_node(key, tree);    if ( x == nil || x->value != key )        return;    if ( x->left != nil && x->right != nil )//找到真正的删除节点    {        y = x->right;        while ( y->left != nil )            y = y->left;    }    else        y = x;    if ( y->left != nil )        z = y->left;    else        z = y->right;        if ( y == tree->root )    {        tree->root = z;        z->parent = nil;    }    else    {        if ( y == y->parent->left )            y->parent->left = z;        else            y->parent->right = z;        if ( z != nil )            z->parent = y->parent;    }    assign(x, y);    if ( y->color == black )        remove_fixup(z, y->parent, tree);    free(y);}
复制代码

在将真正的删除节点剔除时,注意它是否有孩子,如果右孩子,将原来指向真正删除节点的指针指向孩子,不要忘了将更改孩子的父亲。否则将原来指向真正删除节点的指针指向nil。

接下来删除调整

删除调整的实现有很多种,不过答题思路都是一样的。如果自己写的话,需要把各种情况考虑清楚。

复制代码
static void remove_fixup(Node * x, Node * y, Tree * tree){    Node * z;//z是x的兄弟    while ( x != tree->root && x->color == black )    {        if ( x == y->left )        {            z = y->right;            //case 1:    兄弟是红色            //处理方法:            //        1.将兄弟设为黑色            //        2.将父亲设为红色            //        3.以父亲为旋转点,左旋            //        4.重置x的兄弟节点            //    变成case 2, 3, 4            if ( z->color == red )            {                z->color = black;                y->color = red;                left_rotate(y, tree);                z = y->right;            }            //case 2:    兄弟是黑色,并且两个孩子是黑色            //处理方法:            //        1.将兄弟设为红色            //        2.将x设为父亲            if ( z->left->color == black && z->right->color == black )            {                z->color = red;                x = y;                y = x->parent;            }            //case 3:    兄弟是黑色,左孩子是红色,右孩子是黑色            //处理方法;            //        1.将兄弟的左孩子设为黑色            //        2.将兄弟设为红色            //        3.以兄弟为旋转点,右旋            //        4.重新设置兄弟节点            else             {                if ( z->right->color == black )                {                    z->left->color = black;                    z->color = red;                    right_rotate(z, tree);                    z = y->right;                }                //case 4:    兄弟是黑色,右孩子是红色                //处理方法:                //        1.将兄弟的颜色设为父亲的颜色                //        2.将父亲的颜色设为黑色                //        3.将兄弟的右孩子设为黑色                //        4.以父亲为旋转点,左旋                z->color = y->color;                y->color = black;                z->right->color = black;                left_rotate(y, tree);                break;            }        }        else        {            z = y->left;            if ( z->color == red )            {                y->color = red;                z->color = black;                right_rotate(y, tree);                z = y->left;            }            if ( z->left->color == black && z->right->color == black )            {                z->color = red;                x = y;                y = x->parent;            }            else            {                if ( z->left->color == black )                {                    z->right->color = black;                    z->color = red;                    left_rotate(z, tree);                    z = y->left;                }                z->color = y->color;                y->color = black;                z->left->color = black;                right_rotate(y, tree);                break;            }        }    }    if ( x != nil )        x->color = black;}
复制代码

如果x是y的右孩子,操作跟左孩子相同,把left与right交换。

删除操作完成,建议大家在调试把红黑树画出来。

这里附上我的调试代码。

复制代码
#include <stdio.h>#include <stdlib.h>#include <time.h>#include "rbtree.h"void print(Node * x){    printf("%d\t", x->value);    if ( x->color == red )        printf("red\t");    else        printf("black\t");    if ( x->parent )        printf("parent value = %d\n", x->parent->value);    else        printf("\n");}int main(void){    Tree tree;    int i;        srand(time(NULL));    rb_init(&tree);    for ( i = 0; i < 100; i++ )    {        rb_insert(rand()%1000, &tree);    }    rb_treaverse(&tree, print);    for ( i = 0; i < 100; i++ )    {        rb_remove(rand()%1000, &tree);    }//    rb_insert(10, &tree);//    rb_insert(7, &tree);//    rb_insert(8, &tree);//    rb_insert(15, &tree);//    rb_insert(5, &tree);//    rb_insert(6, &tree);//    rb_insert(11, &tree);//    rb_insert(13, &tree);//    rb_insert(12, &tree);//    rb_insert(2, &tree);////    rb_treaverse(&tree, print);//    rb_remove(5, &tree);//    rb_remove(7, &tree);//    rb_remove(6, &tree);//    rb_remove(8, &tree);//    rb_treaverse(&tree, print);//    rb_remove(2, &tree);//    rb_remove(10, &tree);//    rb_remove(11, &tree);//    rb_remove(12, &tree);//    rb_remove(15, &tree);//    rb_remove(13, &tree);    rb_treaverse(&tree, print);    return 0;}
复制代码

前边的两个for循环主要测试红黑树是否有bug,如果发现bug,用下面的插入查找bug。

下边的代码四种情况基本都会用到。

0 0
原创粉丝点击