数据结构之红黑树实现(全)

news2024/11/26 9:58:46

一、红黑树

红黑树是一种自平衡的二叉搜索树,它通过约束节点的颜色和结构来保持平衡。红黑树是由 Rudolf Bayer 在1972年发明的,被认为是一种优秀的平衡树结构,广泛应用于各种数据结构和算法中。

1.红黑树的性质

1. 每个结点是红的或者黑的

2. 根结点是黑的

3. 每个叶子结点是黑的(因为这条性质,一般用叶子结点在代码中被特殊表示)

4. 如果一个结点是红的,则它的两个儿子都是黑的(不存在相邻红色)

5. 从任一节点到叶子节点,所包含的黑色节点数目相同(即黑高度相同)

6. 最长路径长度不超过最短路径长度的2倍(2n-1,一条黑红黑红…一条全黑)

2.红黑树和AVL树

  • 所以红黑树的查询效率略低与AVL的查询
  • 红黑树和AVL插入的速度差不多
  • 红黑树删除的速度比AVL快,因为AVL删除最多需要log(N)次旋转

3.红黑树的应用场景

  • c++ stl map,set(红黑树的封装)
  • 进程调度cfs(用红黑树存储进程的集合,把调度的时间作为key,那么树的左下角时间就是最小的)
  • 内存管理(每次使用malloc的时候都会分配一块小内存出来,那么这么块就是用红黑树来存,如何表述一段内存块呢,用开始地址+长度来表示,所以key->开始地址,val->大小)
  • epoll中使用红黑树管理socketfd
  • nginx中使用红黑树管理定时器,中序遍历第一个就是最小的定时器

二、代码实现

1.结构体定义

红黑树是一种特殊的二叉树,所以其实现和二叉树类似。但需要加上color用来标记颜色。实现如下:

#define RED 0
#define BlACK 1

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;//红黑树结点

typedef struct _rbtree {
    rbtree_node *root;//根结点
    rbtree_node *nil;//通用叶子结点
} rbtree;//红黑树

2.红黑树的左旋和右旋

左旋:动三个方向,改6个指针。

        

左旋如上图所示:

        我们需要改的方向为:

                1.between E and S和S

                2.E和S

                3.S和parents

右旋:和左旋原理一样,不做过多赘述。

代码实现如下:

//左旋leftRotate(T,x)---中右->左中
//降低X结点的高度,提高X结点右结点(即Y)的高度。
void _left_rotate(rbtree *T, rbtree_node *x) {
    rbtree_node *y = x->right;
    //1
    x->right = y->left;//x的右子树指向y的左子树
    if (y->left != T->nil) {
        y->left->parent = x;//y的左子树的父节点指向x
    }
    //2
    y->parent = x->parent;//y的父结点指向x的父结点
    if (x->parent == T->nil) {//如果x是根结点
        T->root = y;
    } else if (x == x->parent->left) {
        x->parent->left = y;//本来指向x结点的父指针,改成指向y
    } else {
        x->parent->right = y;
    }
    //3
    y->left = x;//y的左子树指向x结点
    x->parent = y;//x的父节点指向y
}

//右旋
//copy左旋的代码
//left改成right,right改成left
//x改成y,y改成x
void _right_rotate(rbtree *T, rbtree_node *y) {
    rbtree_node *x = y->left;
    //1
    y->left = x->right;
    if (x->right != T->nil) {
        x->right->parent = y;
    }
    //2
    x->parent = y->parent;
    if (y->parent == T->nil) {
        T->root = x;
    } else if (y == y->parent->right) {
        y->parent->right = x;
    } else {
        y->parent->left = x;
    }
    //3
    x->right = y;
    y->parent = x;
}

3.红黑树插入结点与插入维护红黑树的三种情况

(1)插入结点

        插入这个结点之前,原来的红黑树是满足红黑树性质的==,插入就是不断的对比key,最终找到位置。

        那么,我们需要考虑一个问题:那就是插入的结点应该上什么色?

我们通过性质发现:

        如果新结点是黑色,违背了第5条性质

        如果新结点是红色,可能违背第4条性质

第四条性质,我们可以通过旋转与上色的方式修复,所以在我们插入结点的时候,我们始终认为新结点是红色。

代码实现如下:

//因为传入的key可能是字符,可能是整形,所以要提取出来
//这里可以看出,其实可以封装成一个模板
int key_compare(KEY_TYPE a, KEY_TYPE b) {
    //这里假设是int
    if (a > b) {
        return 1;
    } else if (a < b) {
        return -1;
    } else {
        return 0;
    }
}
void rbtree_insert(rbtree *T, rbtree_node *z) {
    //找位置
    rbtree_node *x = T->root;
    rbtree_node *y = T->nil;//y是x的父节点
    while (x != T->nil) {//二分找位置
        y = x;
        if (key_compare(z->key, x->key) < 0) {
            x = x->left;
        } else if (key_compare(z->key, x->key) > 0) {
            x = x->right;
        } else {
            //如果key相等,看自己的业务情景
            //重复插入可以不修改直接退出,可以修改val
            return;
        }
    }
    //插入
    z->parent = y;
    if (y == T->nil) {
        T->root = z;
    } else if (key_compare(z->key, y->key) < 0) {
        y->left = z;
    } else {
        y->right = z;
    }

    z->left = T->nil;
    z->right = T->nil;
    z->color = RED;
    //维护红黑树
    rbtree_insert_fixup(T, z);
}

(2)插入结点后维护红黑树

我们知道新增结点是红色,如果新结点是父节点也是红色,那么就需要维护红黑树了。

此时有几种情况:

        1.父结点是爷结点是左子树

                (1)叔结点是红色的

                        (i)将叔结点和父结点变黑,爷结点变红

                        (ii)将当前结点变成爷结点(因为爷结点是红,爷结点的父节点也可能是红,所以要递归维护)

                (2)叔结点是黑色的且新结点是左子树

                        (i)将父结点变成黑色,爷结点变成红色

                        (ii)以爷结点为中心右旋

                (3)叔结点是黑色的且新结点是右子树

                        (i)以父结点为中心左旋

                        (ii)将父结点变黑色,爷结点变红色

                          (iii)  以爷结点为中心右旋

        2.父结点是爷结点是右子树

                (1)叔结点是红色的

                        (i)将叔结点和父结点变黑,爷结点变红

                        (ii)将当前结点变成爷结点(因为爷结点是红,爷结点的父节点也可能是红,所以要递归维护)

                (2)叔结点是黑色的且新结点是左子树

                        (i)以父结点为中心右旋

                        (ii)将父结点变黑色,爷结点变红色

                          (iii)  以爷结点为中心左旋

                (3)叔结点是黑色的且新结点是右子树

                        (i)将父结点变成黑色,爷结点变成红色

                        (ii)以爷结点为中心左旋

//修复第4条性质
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;
                    rbtree_left_rotate(T, z);
                }
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rbtree_right_rotate(T, z->parent->parent);
            }
        } else {
            // 如果父结点是爷结点是右子树
            rbtree_node *y = z->parent->parent->left;//叔父结点
            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->left) {//新节点是在左边
                    z = z->parent;
                    rbtree_right_rotate(T, z);
                }
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rbtree_left_rotate(T, z->parent->parent);
            }
        }
    }
    //最后别忘了根节点始终是黑色
    T->root->color = BLACK;
}

4.红黑树删除结点与删除维护红黑树的四种情况

(1)删除结点

我们定义:

  • 覆盖结点:z(被指定删除的结点,实际上被覆盖)
  • 删除结点:y(实际上被删除的结点,一般是后继结点)
  • 轴心结点:x(维护红黑树的结点)

红黑树删除结点根据改结点的左右子树分为三种情况:

  1. 没有左右子树
  2. 有且仅有一个子树
  3. 左右子树都有

对不同情况的处理:

  • 情况1:直接删除该结点
  • 情况2:将该结点的唯一子树挂到父结点上,然后删除该结点
  • 情况3:找一个删除结点y(后继结点)覆盖 指定结点z,然后删除 删除结点y,对于这个删除结点y来说,它的情况一定是情况1或情况2
rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {
    while (x->left != T->nil) {
        x = x->left;
    }
    return x;
}

//找后继结点
rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) {
    rbtree_node *y = x->parent;
    //右子树不为空,则找右子树中最左的元素
    if (x->right != T->nil) {
        return rbtree_mini(T, x->right);
    }
    //找到结点第一个父结点
    while ((y != T->nil) && (x == y->right)) {
        x = y;
        y = y->parent;
    }
    return y;
}

//覆盖结点z
//删除结点y
//轴心结点x
rbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) {
    rbtree_node *y = T->nil;
    rbtree_node *x = T->nil;

    if ((z->left == T->nil) || (z->right == T->nil)) {
        y = z;//如果没有孩子或只有一个
    } else {//如果有两个子树则找后继
        y = rbtree_successor(T, z);
    }
    //一般x是y的右子树,找到轴心结点
    if (y->left != T->nil) {
        x = y->left;
    } else if (y->right != T->nil) {
        x = y->right;
    }
    //将该结点的唯一子树挂到父结点上,然后删除该结点
    x->parent = y->parent;
    if (y->parent == T->nil) {//根结点
        T->root = x;
    } else if (y == y->parent->left) {
        y->parent->left = x;
    } else {
        y->parent->right = x;
    }
    //进行覆盖操作
    if (y != z) {
        z->key = y->key;
        z->value = y->value;
    }
    //黑色才需要调整
    if (y->color == BLACK) {
        rbtree_delete_fixup(T, x);
    }
    return y;
}

(2)维护红黑树

删除一个结点,该结点是什么颜色的时候才需要维护红黑树呢?

  • 如果是红色,没有违反任何性质。所以如果是红色直接删除即可,无需维护
  • 如果是黑色,黑色被删除,那么必定违反第5条性质,破坏了黑高,所以我们需要针对这一情况进行维护

 如果当前结点是父结点的左子树的情况,可以归纳出来四种情况。

当前结点是父结点的左子树的情况

        1. 当前结点的兄弟结点是红色的

                (i)兄弟节点变成黑色

                (ii)父节点变成红色

                (iii)父节点做左旋

                (iiii)将兄弟结点调整为父节点的右子树

        2. 当前结点的兄弟结点是黑色的,而且兄弟结点的两个孩子结点都是黑色的

                (i)兄弟节点变成红色

                (ii)轴心结点变为父节点

        3. 当前结点的兄弟结点是黑色的,而且兄弟结点的左孩子是红色的,右孩子是黑色的

                (i)将左孩子涂黑

                (ii)将兄弟节点变红

                (iii)对兄弟节点右旋

                (iiii)将兄弟结点调整为父节点的右子树

                (iiiii)现在情况3就会变成情况4,接着做情况4的步骤

        4. 当前结点的兄弟结点是黑色的,而且兄弟结点的左孩子是黑色的,右孩子是红色的

                (i)将兄弟节点换成父节点的颜色

                (ii)把父节点和兄弟节点的右孩子涂黑

                (iii)对父节点做左旋

                (iiii)设置x指针,指向根节点

当前结点是父结点的右子树的情况

        1. 当前结点的兄弟结点是红色的

                (i)兄弟节点变成黑色

                (ii)父节点变成红色

                (iii)父节点做右旋

                (iiii)将兄弟结点调整为父节点的左子树

        2. 当前结点的兄弟结点是黑色的,而且兄弟结点的两个孩子结点都是黑色的

                (i)兄弟节点变成红色

                (ii)轴心结点变为父节点

        3. 当前结点的兄弟结点是黑色的,而且兄弟结点的左孩子是黑色的,右孩子是红色的

                (i)将右孩子变黑

                (ii)将兄弟节点变红

                (iii)对兄弟结点左旋

                (iiii)将兄弟结点调整为父节点的左子树

                  (iiiii)现在情况3就会变成情况4,接着做情况4的步骤

        4. 当前结点的兄弟结点是黑色的,而且兄弟结点的左孩子是红色的,右孩子是黑色的

                (i)将兄弟节点换成父节点的颜色

                (ii)把父节点和兄弟节点的左孩子变黑

                (iii)对父节点做右旋

                (iiii)将轴心结点调整为根结点

void rbtree_delete_fixup(rbtree *T, rbtree_node *x) {
    //如果x是红色,变成黑色,如果x是黑色,需要调整
    while ((x != T->root) && (x->color == BLACK)) {
        //当前结点是父结点的左子树
        if (x == x->parent->left) {
            //兄弟节点
            rbtree_node *w = x->parent->right;
            // 情况1:兄弟节点为红色
            if (w->color == RED) {
                // 兄弟节点变成黑色
                w->color = BLACK;
                // 父节点变成红色
                x->parent->color = RED;
                // 父节点做左旋
                rbtree_left_rotate(T, x->parent);
                //将兄弟结点调整为父节点的右子树
                w = x->parent->right;
            }
            // 情况2:兄弟节点是黑色的,且兄弟的左孩子和右孩子都是黑色
            if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
                // 兄弟节点变成红色
                w->color = RED;
                // 轴心结点变为父节点
                x = x->parent;
            } else {
                // 情况3:x的兄弟节点是黑色的,兄弟的左孩子是红色,右孩子是黑色
                if (w->right->color == BLACK) {
                    // 将左孩子涂黑
                    w->left->color = BLACK;
                    // 将兄弟节点变红
                    w->color = RED;
                    // 对兄弟节点右旋
                    rbtree_right_rotate(T, w);
                    // 重新设置x的兄弟节点
                    w = x->parent->right;
                }
                // 情况4:x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的
                // 将兄弟节点换成父节点的颜色
                w->color = x->parent->color;
                // 把父节点和兄弟节点的右孩子涂黑
                x->parent->color = BLACK;
                w->right->color = BLACK;
                // 对父节点做左旋
                rbtree_left_rotate(T, x->parent);
                // 设置x指针,指向根节点
                x = T->root;
            }

        } else {//当前结点是父结点的右子树
            //兄弟节点
            rbtree_node *w = x->parent->left;
            //情况1:兄弟结点为红色
            if (w->color == RED) {
                // 兄弟节点变成黑色
                w->color = BLACK;
                // 父节点变成红色
                x->parent->color = RED;
                // 父节点做右旋
                rbtree_right_rotate(T, x->parent);
                //将兄弟结点调整为父节点的左子树
                w = x->parent->left;
            }
            // 情况2:兄弟节点是黑色的,且兄弟的左孩子和右孩子都是黑色
            if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
                // 兄弟节点变成红色
                w->color = RED;
                // 轴心结点变为父节点
                x = x->parent;
            } else {
                // 情况3:x的兄弟结点是黑色的,兄弟的左孩子是黑色,右孩子是红色
                if (w->left->color == BLACK) {
                    //将右孩子变黑
                    w->right->color = BLACK;
                    //将兄弟节点变红
                    w->color = RED;
                    //对兄弟结点左旋
                    rbtree_left_rotate(T, w);
                    //将兄弟结点调整为父节点的左子树
                    w = x->parent->left;
                }
                // 情况4:x的兄弟结点是黑色的,兄弟的左孩子是红色,右孩子是黑色
                // 将兄弟节点换成父节点的颜色
                w->color = x->parent->color;
                // 把父节点和兄弟节点的左孩子变黑
                x->parent->color = BLACK;
                w->left->color = BLACK;
                // 对父节点做右旋
                rbtree_right_rotate(T, x->parent);
                //将轴心结点调整为根结点
                x = T->root;
            }
        }
    }
    // 继承节点变为黑色,为了弥补失去的黑高
    x->color = BLACK;
}

三、完整代码及测试用例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define RED 0
#define BLACK 1

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;//红黑树结点

typedef struct _rbtree {
    rbtree_node* root;//根结点
    rbtree_node* nil;//通用叶子结点(null)
} rbtree;//红黑树

//函数声明
void rbtree_delete_fixup(rbtree* T, rbtree_node* x);
void rbtree_insert_fixup(rbtree* T, rbtree_node* z);

//左旋leftRotate(T,x)---中右->左中
//需要改变6根指针
//降低X结点的高度,提高X结点右结点(即Y)的高度。
void rbtree_left_rotate(rbtree* T, rbtree_node* x) {
    rbtree_node* y = x->right;
    //1
    x->right = y->left;//x的右子树指向y的左子树
    if (y->left != T->nil) {
        y->left->parent = x;//y的左子树的父节点指向x
    }
    //2
    y->parent = x->parent;//y的父结点指向x的父结点
    if (x->parent == T->nil) {//如果x是根结点
        T->root = y;
    }
    else if (x == x->parent->left) {
        x->parent->left = y;//本来指向x结点的父指针,改成指向y
    }
    else {
        x->parent->right = y;
    }
    //3
    y->left = x;//y的左子树指向x结点
    x->parent = y;//x的父节点指向y
}

//右旋
//copy左旋的代码
//left改成right,right改成left
//x改成y,y改成x
void rbtree_right_rotate(rbtree* T, rbtree_node* y) {
    rbtree_node* x = y->left;
    //1
    y->left = x->right;
    if (x->right != T->nil) {
        x->right->parent = y;
    }
    //2
    x->parent = y->parent;
    if (y->parent == T->nil) {
        T->root = x;
    }
    else if (y == y->parent->right) {
        y->parent->right = x;
    }
    else {
        y->parent->left = x;
    }
    //3
    x->right = y;
    y->parent = x;
}

//因为传入的key可能是字符,可能是整形,所以要提取出来
//这里可以看出,其实可以封装成一个模板
int key_compare(KEY_TYPE a, KEY_TYPE b) {
    //这里假设是int
    if (a > b) {
        return 1;
    }
    else if (a < b) {
        return -1;
    }
    else {
        return 0;
    }
}
//插入结点
void rbtree_insert(rbtree* T, rbtree_node* z) {
    //找位置
    rbtree_node* x = T->root;
    rbtree_node* y = T->nil;//y是x的父节点
    while (x != T->nil) {//二分找位置
        y = x;
        if (key_compare(z->key, x->key) < 0) {
            x = x->left;
        }
        else if (key_compare(z->key, x->key) > 0) {
            x = x->right;
        }
        else {
            //如果key相等,看自己的业务情景
            //重复插入可以不修改直接退出,可以修改val
            return;
        }
    }
    //插入
    z->parent = y;
    if (y == T->nil) {
        T->root = z;
    }
    else if (key_compare(z->key, y->key) < 0) {
        y->left = z;
    }
    else {
        y->right = z;
    }

    z->left = T->nil;
    z->right = T->nil;
    z->color = RED;
    //维护红黑树
    rbtree_insert_fixup(T, z);
}


//修复第4条性质
//插入节点之后,如果破坏了红黑树的性质,需要修复
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;
                    rbtree_left_rotate(T, z);
                }
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rbtree_right_rotate(T, z->parent->parent);
            }
        }
        else {
            // 如果父结点是爷结点是右子树
            rbtree_node* y = z->parent->parent->left;//叔父结点
            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->left) {//新节点是在左边
                    z = z->parent;
                    rbtree_right_rotate(T, z);
                }
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rbtree_left_rotate(T, z->parent->parent);
            }
        }
    }
    //最后别忘了根节点始终是黑色
    T->root->color = BLACK;
}


rbtree_node* rbtree_mini(rbtree* T, rbtree_node* x) {
    while (x->left != T->nil) {
        x = x->left;
    }
    return x;
}

//找后继结点
rbtree_node* rbtree_successor(rbtree* T, rbtree_node* x) {
    rbtree_node* y = x->parent;
    //右子树不为空,则找右子树中最左的元素
    if (x->right != T->nil) {
        return rbtree_mini(T, x->right);
    }
    //找到结点第一个父结点
    while ((y != T->nil) && (x == y->right)) {
        x = y;
        y = y->parent;
    }
    return y;
}

//覆盖结点z
//删除结点y
//轴心结点x
rbtree_node* rbtree_delete(rbtree* T, rbtree_node* z) {
    rbtree_node* y = T->nil;
    rbtree_node* x = T->nil;

    if ((z->left == T->nil) || (z->right == T->nil)) {
        y = z;//如果没有孩子或只有一个
    }
    else {//如果有两个子树则找后继
        y = rbtree_successor(T, z);
    }
    //一般x是y的右子树,找到轴心结点
    if (y->left != T->nil) {
        x = y->left;
    }
    else if (y->right != T->nil) {
        x = y->right;
    }
    //将该结点的唯一子树挂到父结点上,然后删除该结点
    x->parent = y->parent;
    if (y->parent == T->nil) {//根结点
        T->root = x;
    }
    else if (y == y->parent->left) {
        y->parent->left = x;
    }
    else {
        y->parent->right = x;
    }
    //进行覆盖操作
    if (y != z) {
        z->key = y->key;
        z->value = y->value;
    }
    //黑色才需要调整
    if (y->color == BLACK) {
        rbtree_delete_fixup(T, x);
    }
    return y;
}


void rbtree_delete_fixup(rbtree* T, rbtree_node* x) {
    //如果x是红色,变成黑色,如果x是黑色,需要调整
    while ((x != T->root) && (x->color == BLACK)) {
        //当前结点是父结点的左子树
        if (x == x->parent->left) {
            //兄弟节点
            rbtree_node* w = x->parent->right;
            // 情况1:兄弟节点为红色
            if (w->color == RED) {
                // 兄弟节点变成黑色
                w->color = BLACK;
                // 父节点变成红色
                x->parent->color = RED;
                // 父节点做左旋
                rbtree_left_rotate(T, x->parent);
                //将兄弟结点调整为父节点的右子树
                w = x->parent->right;
            }
            // 情况2:兄弟节点是黑色的,且兄弟的左孩子和右孩子都是黑色
            if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
                // 兄弟节点变成红色
                w->color = RED;
                // 轴心结点变为父节点
                x = x->parent;
            }
            else {
                // 情况3:x的兄弟节点是黑色的,兄弟的左孩子是红色,右孩子是黑色
                if (w->right->color == BLACK) {
                    // 将左孩子涂黑
                    w->left->color = BLACK;
                    // 将兄弟节点变红
                    w->color = RED;
                    // 对兄弟节点右旋
                    rbtree_right_rotate(T, w);
                    // 重新设置x的兄弟节点
                    w = x->parent->right;
                }
                // 情况4:x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的
                // 将兄弟节点换成父节点的颜色
                w->color = x->parent->color;
                // 把父节点和兄弟节点的右孩子涂黑
                x->parent->color = BLACK;
                w->right->color = BLACK;
                // 对父节点做左旋
                rbtree_left_rotate(T, x->parent);
                // 设置x指针,指向根节点
                x = T->root;
            }

        }
        else {//当前结点是父结点的右子树
            //兄弟节点
            rbtree_node* w = x->parent->left;
            //情况1:兄弟结点为红色
            if (w->color == RED) {
                // 兄弟节点变成黑色
                w->color = BLACK;
                // 父节点变成红色
                x->parent->color = RED;
                // 父节点做右旋
                rbtree_right_rotate(T, x->parent);
                //将兄弟结点调整为父节点的左子树
                w = x->parent->left;
            }
            // 情况2:兄弟节点是黑色的,且兄弟的左孩子和右孩子都是黑色
            if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
                // 兄弟节点变成红色
                w->color = RED;
                // 轴心结点变为父节点
                x = x->parent;
            }
            else {
                // 情况3:x的兄弟结点是黑色的,兄弟的左孩子是黑色,右孩子是红色
                if (w->left->color == BLACK) {
                    //将右孩子变黑
                    w->right->color = BLACK;
                    //将兄弟节点变红
                    w->color = RED;
                    //对兄弟结点左旋
                    rbtree_left_rotate(T, w);
                    //将兄弟结点调整为父节点的左子树
                    w = x->parent->left;
                }
                // 情况4:x的兄弟结点是黑色的,兄弟的左孩子是红色,右孩子是黑色
                // 将兄弟节点换成父节点的颜色
                w->color = x->parent->color;
                // 把父节点和兄弟节点的左孩子变黑
                x->parent->color = BLACK;
                w->left->color = BLACK;
                // 对父节点做右旋
                rbtree_right_rotate(T, x->parent);
                //将轴心结点调整为根结点
                x = T->root;
            }
        }
    }
    // 继承节点变为黑色,为了弥补失去的黑高
    x->color = BLACK;
}

rbtree_node* rbtree_search(rbtree* T, KEY_TYPE key) {
    rbtree_node* node = T->root;
    while (node != T->nil) {
        if (key < node->key) {
            node = node->left;
        }
        else if (key > node->key) {
            node = node->right;
        }
        else {
            return node;
        }
    }
    return T->nil;
}

void rbtree_traversal(rbtree* T, rbtree_node* node) {
    if (node != T->nil) {
        rbtree_traversal(T, node->left);
        printf("key:%d, color:%d\n", node->key, node->color);
        rbtree_traversal(T, node->right);
    }
}

int main() {
    int keyArray[20] = { 24, 25, 13, 35, 23, 26, 67, 47, 38, 98, 20, 19, 17, 49, 12, 21, 9, 18, 14, 15 };

    rbtree* T = (rbtree*)malloc(sizeof(rbtree));
    if (T == NULL) {
        printf("malloc failed\n");
        return -1;
    }

    T->nil = (rbtree_node*)malloc(sizeof(rbtree_node));
    T->nil->color = BLACK;
    T->root = T->nil;

    rbtree_node* node = T->nil;
    int i = 0;
    for (i = 0; i < 20; i++) {
        node = (rbtree_node*)malloc(sizeof(rbtree_node));
        node->key = keyArray[i];
        node->value = NULL;
        rbtree_insert(T, node);

    }
    rbtree_traversal(T, T->root);
    printf("****************************************\n");

    for (i = 0; i < 20; i++) {
        rbtree_node* node = rbtree_search(T, keyArray[i]);
        rbtree_node* cur = rbtree_delete(T, node);
        free(cur);
        rbtree_traversal(T, T->root);
        printf("----------------------------------------\n");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2193100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

detectron2/data/catalog.py源码笔记

公开接口是DatasetCatalog对象&#xff0c;MetadataCatalog对象和Metadata类 DatasetCatalog.register(name, func) #用于注册函数 DatasetCatalog.get(name) #返回函数调用结果return func() DatasetCatalog.list() #return list(self.keys()) Datase…

Windows系统编程(三)进程与线程二

进程与线程 进程&#xff1a;直观的说就是任务管理器中各种正在运行的程序。对于操作系统来说&#xff0c;进程仅仅是一个数据结构&#xff0c;并不会真实的执行代码 线程&#xff1a;通常被称作但并不真的是轻量级进程或实际工作中的进程&#xff0c;它会真实的执行代码。每…

强制删除了windows自带的edge浏览器,重装不了怎么办【已解决】

#最近我的edge浏览器出了点问题&#xff0c;点击打不开但是能在下面的任务栏看到他开启了&#xff0c;就是不能够显示在桌面&#xff0c;小窗口叫我配置设置。 我不懂&#xff0c;感觉很烦&#xff0c;就把他强制卸载了。但是windows是不允许将他卸载的&#xff0c;使用window…

LeetCode讲解篇之695. 岛屿的最大面积

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们遍历二维矩阵&#xff0c;如果当前格子的元素为1进行深度优先搜索&#xff0c;将搜索过的格子置为0&#xff0c;防止重复访问&#xff0c;然后对继续深度优先搜索上下左右中为1的格子 题解代码 func maxAr…

sublime配置(竞赛向)

我也想要有jiangly一样的sublime 先决条件 首先&#xff0c;到官网上下载最新的sublime4&#xff0c;然后在mingw官网上下载最新的mingw64 mingw64官网&#xff1a;左边菜单栏点击dowloads,然后选择MinGW-W64-builds(可能会有点慢)——然后有时候会变成选LLVM-minGW,接着选择…

Vue入门-指令学习-v-show和v-if

v-show&#xff1a; 作用&#xff1a;控制元素的显示隐藏 语法&#xff1a;v-show"表达式" 表达式值true显示&#xff0c;false隐藏 v-if 作用&#xff1a;控制元素的显示隐藏&#xff08;条件渲染&#xff09; 语法&#xff1a; vif"表达式" 表达式tr…

【Mybatis篇】Mybatis的注解开发

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】&#xff0c;【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 文章目录 &#x1f3af; Select注解 …

Nuxt.js 应用中的 app:suspense:resolve 钩子详解

title: Nuxt.js 应用中的 app:suspense:resolve 钩子详解 date: 2024/10/6 updated: 2024/10/6 author: cmdragon excerpt: app:suspense:resolve 是一个强大的钩子,允许开发者在异步数据解析完成后的最后一步执行必要的处理。通过合理使用该钩子,我们可以优化组件的渲染…

AI赋能,旅游新纪元,看旅游大厂携程的AI实践

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 国庆假期马上过去了&#xff0c;2024年国庆…

智能制造的人机料法环的内涵

在生产和管理领域,有个很重要的概念叫 “人、机、料、法、环”。 “人” 就是参与其中的人员,他们的技能、态度、责任心等对事情的结果影响很大; “机” 指的是机器设备和工具等,就像干活要用的家伙事儿,好不好用、正不正常直接关系到工作的效率和质量; “料” 呢,就…

【动态规划-最长公共子序列(LCS)】【hard】【科大讯飞笔试最后一题】力扣115. 不同的子序列

给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 10^9 7 取模。 示例 1&#xff1a; 输入&#xff1a;s “rabbbit”, t “rabbit” 输出&#xff1a;3 解释&#xff1a; 如下所示, 有 3 种可以从 s 中得到 “rabbit”…

手撕正弦-余弦位置编码(Sinusoidal Positional Encoding)

改写后的代码&#xff1a; import torch import math import torch.nn as nnclass PositionalEncoder(nn.Module):def __init__(self, d_model, max_seq_len80):super().__init__()self.d_model d_model# 根据 pos 和 i 创建一个常量 PE 矩阵pe torch.zeros(max_seq_len, d_…

【unity游戏开发】彻底理解AnimatorStateInfo,获取真实动画长度

前言 前置知识&#xff1a;设置参数后&#xff0c;下一个循环才会切换对应动画&#xff0c;所以在下一个循环获取真实的动画长度 AnimatorStateInfo是结构体&#xff01;值类型&#xff0c;要不断重复获取才是最新的 主要是自动设置trigger切换的动画自动切回上一个动画&#x…

Linux命令:用于管理 Linux 系统中用户组的命令行工具gpasswd详解

目录 一、概述 二、使用方法 1、 基本语法 2、 常用选项 &#xff08;1&#xff09;无选项&#xff08;仅组名&#xff09; &#xff08;2&#xff09;-a 用户 &#xff08;3&#xff09;-d 用户 &#xff08;4&#xff09;-M 用户列表 &#xff08;5&#xff09;-A 管…

系统架构设计师-下午案例题(2021年下半年)

1.试题一(共25分) 阅读以下关于软件架构设计与评估的叙述在答题纸上回答问题1和问题2。 【说明】某公司拟开发一套机器学习应用开发平台支持用户使用浏览器在线进行基于机器学习的智能应用开发活动。该平台的核心应用场景是用户通过拖拽算法组件灵活定义机器学习流程&#xf…

【Linux】信号知识三把斧——信号的产生、保存和处理

目录​​​​​​​ 1、关于信号的前置知识 1.1.什么是信号&#xff1f; 1.2.为什么要学习信号&#xff1f; 1.3.如何学习信号&#xff1f; 1.4.一些常见的信号 1.5.信号的处理方式 1.6.为什么每一个进程都可以系统调用&#xff1f; 2.信号的产生 2.1.kill命令产生信号…

[Python学习日记-40] 函数进阶之装饰器

[Python学习日记-40] 函数进阶之装饰器 简介 引子 什么是装饰器 装饰器终结版 装饰器的层层叠加 简介 在前面铺垫了这么多终于该讲到重点了&#xff0c;前面说的匿名函数、高阶函数、闭包等等都是为了这篇文章所讲的装饰器而使用的&#xff0c;本篇文章将会一一个故事通俗…

个人网站,怎么操作才能提升个人网站的流量

运营个人网站以提升流量是一个综合性的过程&#xff0c;涉及内容优化、技术调整、用户体验提升以及外部推广等多个方面。以下是一些专业建议&#xff0c;旨在帮助个人网站运营者有效提升网站流量&#xff1a; 1.精准关键词研究与优化 -关键词研究&#xff1a;利用工具如谷歌…

关于 Python 3.13 你所需要知道的几点

什么是全局解释器锁 (GIL)&#xff1f; 自20世纪80年代末&#xff0c;Guido Van Rossum在荷兰阿姆斯特丹东部的一个科技园区开始开发Python编程语言&#xff0c;它最初被设计为一种单线程的解释型语言。这到底是什么意思呢&#xff1f; 你可能会听说&#xff0c;编程语言分为解…

C++-容器适配器- stack、queue、priority_queue和仿函数

目录 1.什么是适配器 2.deque 1.简单了解结构 2.deque的缺陷 3.为什么选择deque作为stack和queue的底层默认容器 3.stack&#xff08;栈&#xff09; 4.queue&#xff08;队列&#xff09; 5.仿函数 6.priority_queue&#xff08;优先级队列&#xff09;&#xff08;堆…