• 红黑树(三)删除


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

      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	", x->value);
        if ( x->color == red )
            printf("red	");
        else
            printf("black	");
        if ( x->parent )
            printf("parent value = %d
    ", x->parent->value);
        else
            printf("
    ");
    }
    
    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。

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

  • 相关阅读:
    MySQL ——索引原理与慢查询优化(Day45)
    mysql 练习题(Day44)
    MySQL 多表查询(Day43)
    MySQL 单表查询(Day42)
    MySQL -表完整性约束(Day41)
    回调函数
    进程池
    共享数据, 信号量(了解),事件(了解)
    管道
    python并发编程之多进程
  • 原文地址:https://www.cnblogs.com/ITgaozy/p/5180491.html
Copyright © 2020-2023  润新知