nginx--rbtree使用详解

      红黑数的性质这里就不多说了网上很多,主要看看nginx是如何定义的rbtree和怎么使用它

      

rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));   //从共享内存中创建一颗红黑树

sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));   //创建了一个红黑树的节点作为哨兵节点

ngx_rbtree_init(ctx->rbtree, ctx->sentinel, ngx_http_srs_hook_rbtree_insert_value); //初始化这颗红黑树

主要看下ngx_rbtree_init的实现:

#define ngx_rbtree_init(tree, s, i)                                           \
    ngx_rbtree_sentinel_init(s);                                              \
    (tree)->root = s;                                                         \
    (tree)->sentinel = s;                                                     \
    (tree)->insert = i

关键的就是参数i,这个是个函数指针,作为红黑树的插入节点时候的算法,这个需要用户自己实现

typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

第二个参数就是需要插入的节点数据,第三个参数是哨兵节点

接下去看下怎么实现这个插入函数(ps: nginx很多模块中已经有了rbtree插入的实现,我也是参考写的)

static void 
ngx_http_srs_hook_rbtree_insert_value(ngx_rbtree_node_t *temp,   ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) {

	ngx_rbtree_node_t           **p;
    p = NULL;
    if(node == NULL) {
        return;
    }
    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

            break;  //目前的实现不支持相同的key的节点
        }

        if (*p == sentinel) {
            break;
        }

        temp = *p;
    }

    *p = node;
    node->parent = temp;
    node->left = sentinel;
    node->right = sentinel;
    ngx_rbt_red(node);      //最后就是要让插入的节点是红色的(这是红黑树的性质)
	 
}

对于使用者只需要负责插入的算法, 对于红黑数的反转等问题nginx都为大家做了


现在看看怎么进行节点的插入的

typedef struct{              
    ngx_rbtree_node_t node;
    void *data;
}channel;     //定义的红黑树节点

channel findchannel;
ngx_rbtree_insert(srshookctx->rbtree,&(findchannel->node));

看下ngx_rbtree_insert的实现:

ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{
    ngx_rbtree_node_t  **root, *temp, *sentinel;

    /* a binary tree insert */

    root = (ngx_rbtree_node_t **) &tree->root;
    sentinel = tree->sentinel;

    if (*root == sentinel) {
        node->parent = NULL;
        node->left = sentinel;
        node->right = sentinel;
        ngx_rbt_black(node);
        *root = node;

        return;
    }

    tree->insert(*root, node, sentinel);     //这里就调用了我们之前设定的插入算法的函数

    /* re-balance tree */                    //数据插入之后, nginx就为我们做了很复杂的红黑树的翻转来保持树的平衡

    while (node != *root && ngx_rbt_is_red(node->parent)) {

        if (node->parent == node->parent->parent->left) {
            temp = node->parent->parent->right;

            if (ngx_rbt_is_red(temp)) {
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {
                if (node == node->parent->right) {
                    node = node->parent;
                    ngx_rbtree_left_rotate(root, sentinel, node);
                }

                ngx_rbt_black(node->parent);
                ngx_rbt_red(node->parent->parent);
                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);
            }

        } else {
            temp = node->parent->parent->left;

            if (ngx_rbt_is_red(temp)) {
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {
                if (node == node->parent->left) {
                    node = node->parent;
                    ngx_rbtree_right_rotate(root, sentinel, node);
                }

                ngx_rbt_black(node->parent);
                ngx_rbt_red(node->parent->parent);
                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);
            }
        }
    }

    ngx_rbt_black(*root);
}


这样红黑数的插入就完成了,那如何来查找树里的节点呢?这里我实现了一个查找节点的函数

static requestchannel_st*
ngx_http_channel_tree_lookup(ngx_http_srs_hook_ctx_t *srsctx, uint64_t key)
{
    ngx_rbtree_key_t             node_key;
    ngx_rbtree_node_t           *node;
    ngx_rbtree_node_t           *sentinel;
    requestchannel_st                  *fcn;

    node_key = key;

    node = srsctx->rbtree->root;      //这里记录下要查找的树的根节点
    sentinel = srsctx->rbtree->sentinel;  //记录下哨兵节点

    while (node != sentinel) {

        if (node_key < node->key) {       //通过左右子树查找key一样的节点
            node = node->left;
            continue;
        }

        if (node_key > node->key) {
            node = node->right;
            continue;
        }

        /* node_key == node->key */

        fcn = (requestchannel_st *) node;    //找到节点就转成节点数据结构,返回

        return fcn;
    }

    /* not found */

    return NULL;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值