【高阶数据结构】红黑树

1. 使用场景

  • Linux进程调度CFS
  • Nginx Timer事件管理
  • Epoll事件块的管理

2. 性质

  • 每一个节点是红色或者黑色
  • 根节点一定是黑色
  • 每个叶子节点是黑色
  • 如果一个节点是红色,那么它的两个儿子节点都是黑色
  • 从任意一个节点到其每个叶子节点的所有路径都包含相同数目的黑色节点

3. 结点定义

typedef int KEY_TYPE;

typedef struct _rbtree_node
{
    
    
    unsigned char color;
    struct _rbtree_node *left;
    struct _rbtree_node *right;
    struct _rbtree_node *parent;

    KEY_TYPE key;
    void *value;
}rbtree_node;

4. 结点旋转

在这里插入图片描述

左旋实现

void _left_rotate(rbtree *T, rbtree_node *x)
{
    
    
    rbtree_node *y = x->right;

    x->right = y->left;
    if (y->left != T->nil)
    {
    
    
        y->left->parent = x;
    }

    y->parent = x->parent;
    if (x->parent == T->nil)
    {
    
    
        T->root == y;
    }
    else if (x == x->parent->left)
    {
    
    
        x->parent->left = y;
    }
    else if (x == x->parent->right)
    {
    
    
        x->parent->right = y;
    }

    y->left = x;
    x->parent = y;
}

右旋实现

void _right_rotate(rbtree *T, rbtree_node *y)
{
    
    
    rbtree_node *x = y->left;

    y->left = x->right;
    if (x->right != T->nil)
    {
    
    
        x->right->parent = y;
    }

    x->parent = y->parent;
    if (y->parent == T->nil)
    {
    
    
        T->root = x;
    }
    else if (y == y->parent->left)
    {
    
    
         y->parent->left = x;
    }
    else if (y == y->parent->right)
    {
    
    
        y->parent->right = x;
    }

    x->right = y;
    y->parent = x;
}

5. 结点插入

  1. 将新节点的颜色设为红色,然后按照二叉查找树的规则插入到合适的位置

    如果设置成黑色的话,则必定会违反上述五条性质中的第五条

  2. 如果新节点是根节点,那么将其颜色改为黑色,结束

  3. 如果新节点的父节点是黑色,那么不需要做任何调整,结束

  4. 如果新节点的父节点和叔叔节点(父节点的兄弟节点)都是红色,那么将它们的颜色改为黑色,将祖父节点(父节点的父节点)的颜色改为红色,然后把祖父节点作为新的当前节点,重复步骤2和3

  5. 如果新节点的父节点是红色,但叔叔节点是黑色或不存在,那么分四种情况进行旋转和变色操作:

    扫描二维码关注公众号,回复: 14630298 查看本文章
    • 新节点是父节点的左子节点,且父节点是祖父节点的左子节点(左左情况),那么对祖父节点进行右旋,并交换祖父和父亲的颜色
    • 新节点是父节点的右子节点,且父节点是祖父节点的左子节点(左右情况),那么对父亲节点进行左旋,并交换新节点和父亲节点的颜色,然后把新节点作为当前节点,转化为左左情况处理
    • 新节点是父亲节点的右子节点,且父亲节点是祖父节点的右子节点(右右情况),那么对祖父节点进行左旋,并交换祖父和父亲的颜色
    • 新节点是父亲节点的左子节点,且父亲节点是祖父节点的右子节点(右左情况),那么对父亲节点进行右旋,并交换新节点和父亲节点的颜色,然后把新节点作为当前节点,转化为右右情况处理
int key_compare(KEY_TYPE *a, KEY_TYPE *b)
{
    
    
    //TODO
}

void rbtree_insert_fixup(rbtree *T, rbtree_node *z)
{
    
    
    while (z->parent->color == RED)
    {
    
    
        if (z->parent == z->parent->parent->left)
        {
    
    
            rbtree_node *y = z->parent->parent->right;
            if (y->color == RED)
            {
    
    
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;

                z = z->parent->parent;
            }
            else
            {
    
    
                if (z == z->parent->right)
                {
    
    
                    z = z->parent;
                    _left_rotate(T, z);
                }

                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                _right_rotate(T, z->parent->parent);
            }
        }
    }
    T->root->color = BLACK;
}

void rbtree_insert(rbtree *T, rbtree_node *z)
{
    
    
    rbtree_node *x = T->root;
    rbtree_node *y = T->nil;

    while (x != T->nil)
    {
    
    
        y = x;
        // 如果是自定义类型,可以实现key_compare接口来进行比较
        if (x->key > z->key)
        {
    
    
            x = x->right;
        }
        else if (x->key < z->key)
        {
    
    
            x = x->left;
        }
        else
        {
    
    
            return;
        }
    }

    z->parent = y;
    if (y == T->nil)
    {
    
    
        T->root = z;
    }
    else if (z->key > y->key)
    {
    
    
        y->right = z;
    }
    else
    {
    
    
        y->left = z;
    }

    z->left = T->nil;
    z->right = T->nil;
    z->color = RED;

    rbtree_insert_fixup(T, z);
}

猜你喜欢

转载自blog.csdn.net/weixin_52665939/article/details/129473071