rbtree

为什么会有红黑树?

是因为二叉查找树,最差的情况会退化成链表。查找效率就成了O(n)

为了保证二叉查找树的查找效率,需要保持二叉树的平衡,引出了平衡二叉树,但是完全平衡的二叉查找树实现复杂,每个节点都需要维护一个高度。所以出现了红黑树,它是利用黑高来保持平衡。

红黑树的定义

  1. 是一个二叉查找树;
  2. 每个节点都是黑色或者红色;
  1. 根节点是黑色的;
  2. 每个叶子节点是黑色的;
  1. 如果一个节点是红色的,那么它的两个儿子必须是黑色的;
  2. 从一个节点出发,到任意一个叶子节点,包含相同数目的黑色节点。

红黑树的使用场景

对于红黑树的使用,主要是基于它的两个特点:

  1. 查找效率O(logn), 适合用于key->value的查找;
  2. 中序遍历是顺序的。

红黑树典型应用:

  1. map, 利用了特点1,将key作为索引存入红黑树,通过key可以查找到value。
  2. nginx中用于定时器等,利用特点2,将定时任务到期的timestamp存入红黑树,通过中序遍历,查找到红黑树中最小的节点,即最近要到期的定时任务。
  1. cfs(Completely Fair Scheduler, 完全公平调度),利用特点2,将进程调度的时间存入红黑树,查找到最小的节点,即调度时间最短的进程,进行调度,可以做到公平调度。
  2. 内存管理,利用特点1,可以快速查找到对应的内存块。

内存管理的红黑树的key是内存首地址?

一个内存块的表述方法:

a. 首地址 + 长度

b. 首地址 + 尾地址

红黑树实现

  1. 定义rbtree_node、rbtree;
  2. 实现左旋、右旋,用于插入新节点后,继续符合红黑树的条件;

需要动3根线,6个指针。

  1. 插入新节点,在插入之后进行修正,时的能够继续符合红黑树的定义

新插入的节点,是红色的,所以只有它的父节点是红色的时候,需要修正。

父节点是红色,分成以下case:

  1. 父节点是祖父节点左子树
  2. 父节点是祖父节点右子树

上面两个case又可以分为3个小case:

对于父节点是祖父节点左子树的情况,分为以下case:

  1. 叔父节点是红色;

将父节点和叔父节点设置为黑色,祖父节点设置为红色,这样祖父节点往下就满足红黑树定义。

接着将祖父节点作为当前节点,继续递归进行修正。

  1. 叔父节点是黑色,当前节点是父节点的右子树

将父节点作为当前节点,并且以它为轴,进行左旋。

  1. 叔父节点是黑色,当前节点是父节点的左子树

将父节点设置为黑色,祖父节点设置为红色,并以祖父节点为轴进行右旋。

对于父节点是祖父节点右子树的情况,也可以分为3个case,将上面的代码copy一份,将left换成right,right换成left就可以了。

左右子树高度最大相差多少?小于2倍。比如左子树高度为9,那么右子树最小为5,最大相差4

​​​​​​​#define RED 0
#define BLACK 1


typedef int KEY_TYPE;

// int key_compare(KEY_TYPE a, KEY_TYPE b) {

// }

// 6项
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;


void _left_rotate(rbtree *T, rbtree_node *x) {

    // 3个方向,6根指针

    rbtree_node *y = x->right;

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

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

    // 3
    y->left = x; // 3.1
    x->parent = y; // 3.2

}

void _right_rotate(rbtree *T, rbtree_node *y) {

    // 3个方向,6根指针

    rbtree_node *x = y->left;

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

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

    // 3
    x->right = y; // 3.1
    y->parent = x; // 3.2

}

void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {

    // if (z->parent->color == RED) {
    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;

                // 祖父节点的父节点可能是红色的,继续while处理
                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);
            }
        } else {
            
        }

    }

    T->root->color = BLACK; // 中间过程有可能把root节点变为红色了,最后需要将它变黑

}


void rbtree_insert(rbtree *T, rbtree_node *z) {

    rbtree_node *x = T->root;
    rbtree_node *y = T->nil;

    while (x != T->nil) {
        y = x; // y是x的父节点
        if (z->key < x->key) { // key的比较规则可以交给红黑树的使用者自定义
            x = x->left;
        } else if (z->key > x->key) {
            x = x->right;
        } else {
            return; // 是否插入相同的key,需要看应用场景
        }

    }

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

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

    rbtree_insert_fixup(T, z);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值