Red-black tree ngx_rbtree_t of Nginx data structure

1. What is a red-black tree?

1.1 Overview

A red-black tree is actually a self-balancing binary search tree.

What is a binary tree? A binary tree is a tree structure in which each node has at most two subtrees, each node can be used to store data, and any one node can access its left and right
subtrees or parent nodes.

What is a binary search tree? A binary search tree, or an empty tree, is a binary tree with the following properties.

  • Each node has a key that is used as a search basis, and the keys of all nodes are different from each other.
  • The keys of all nodes on the left subtree (if any) are less than the key of the root node.
  • The keys of all nodes on the right subtree (if any) are greater than the keys of the root node.
  • Left subtree and right subtree are also binary search trees.

In this way, all element nodes of a binary search tree are ordered. In the case that the shape of the binary tree is relatively balanced, its retrieval efficiency is very high, which is somewhat similar to
the efficiency of binary retrieval of ordered arrays. In general, the query complexity is related to the distance (ie depth) from the target node to the root node. However, continuously adding and deleting
nodes may cause the binary search tree to be very unbalanced. In extreme cases, it will become a singly linked list, and the retrieval efficiency will also become low.

What is a self-balancing binary search tree? When continuously adding and deleting nodes to the binary search tree, the binary search tree itself always maintains a certain
degree of balance through the transformation of its shape, which is a self-balancing binary search tree. A red-black tree is a self-balancing binary search tree.

The elements in the ngx_rbtree_t red-black tree container are all ordered. It supports fast retrieval, insertion, and deletion operations, as well as range query, traversal, and other operations.

1.2 Characteristics of red-black trees

A red-black tree refers to a binary search tree where each node has a color attribute, where the color is red or black. In addition to the general requirements of a binary search tree, the red-black tree
has the following properties:

  • Feature 1: Nodes are red or black.
  • Feature 2: The root node is black.
  • Feature 3: All leaf nodes are black (leafs are NIL nodes, also called "sentry").
  • Feature 4: Both child nodes of each red node are black (there cannot be two consecutive red nodes on all paths from each leaf node to the root node).
  • Property 5: All simple paths from any node to each leaf node contain the same number of black nodes.

These constraints reinforce a key property of red-black trees: the length of the longest possible path from the root node to the leaf node is no greater than twice the length of the shortest possible path, so the tree is roughly
balanced.

Example: After adding elements 1, 6, 8, 11, 13, 15, 17, 22, 25, and 27 to the empty ngx_rbtree_t red-black tree container in turn, the following red-black tree will be formed:

2. Implementation of ngx_rbtree_t red-black tree

2.1 Related Structures

2.1.1 ngx_rbtree_node_t: node structure of red-black tree

typedef ngx_uint_t  ngx_rbtree_key_t;
typedef ngx_int_t   ngx_rbtree_key_int_t;

typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;

struct ngx_rbtree_node_s {
    /* 无符号整型的关键字 */
    ngx_rbtree_key_t       key;
    /* 左子节点 */
    ngx_rbtree_node_t     *left;
    /* 右子节点 */
    ngx_rbtree_node_t     *right;
    /* 父节点 */
    ngx_rbtree_node_t     *parent;
    /* 节点的颜色,0 表示黑色,1 表示红色 */
    u_char                 color;
    /* 仅 1 字节的节点数据。由于表示的空间太小,一般很少使用 */
    u_char                 data;
};

ngx_rbtree_node_t is a data structure that must be used in the implementation of red-black tree. Generally, it is placed in the first member of the structure, so that it is convenient to
force the custom structure into ngx_rbtree_node_t type.
The key member in the ngx_rbtree_node_t structure is the key of each red-black tree node, and it must be an integer. The sorting of the red-black tree is mainly based on the key members
(after customizing the ngx_rbtree_insert_pt method, other members of the node can also affect the shape of the red-black tree based on the key sorting).

2.1.2 ngx_rbtree_t: red-black tree container

typedef struct ngx_rbtree_s  ngx_rbtree_t;

/* 为解决不同节点含有相同关键字的元素冲突问题,红黑树设置了 ngx_rbtree_insert_pt 
 * 指针,这样可灵活地添加冲突元素 */
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

struct ngx_rbtree_s {
    /* 指树的根节点。注意,根节点也是数据元素 */
    ngx_rbtree_node_t     *root;
    /* 指向 NIL 哨兵节点 */
    ngx_rbtree_node_t     *sentinel;
    /* 表示红黑树添加元素的函数指针,它决定在添加新节点时的行为究竟是替换还是新增 */
    ngx_rbtree_insert_pt   insert;
};

A red-black tree is a general-purpose data structure, and its nodes (or elements called containers) can be any structure that contains basic red-black tree nodes. For different
structures, in many cases, different nodes are allowed to have the same keyword. For example, different strings may hash to the same key, then their key in the red-
black tree is the same, but they are different nodes, so that the original same name cannot be overwritten when adding key node, but exists as a newly inserted
node. So abstracting the method of adding elements out of the ngx_rbtree_insert_pt function pointer works well for this idea.

2.2 Provided interfaces

2.2.1 ngx_rbtree_init: Initialize a red-black tree

/* 设置该节点颜色为红色 */
#define ngx_rbt_red(node)               ((node)->color = 1)
/* 设置该节点颜色为黑色 */
#define ngx_rbt_black(node)             ((node)->color = 0)

/* a sentinel must be black */
/* 初始化一个哨兵节点,哨兵节点(即叶子节点)一定是黑色的 */
#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

/*
 * 参数含义:
 * - tree:是红黑树容器的指针
 * - s:是哨兵节点的指针
 * - i:是ngx_rbtree_insert_pt类型的节点添加方法
 *
 * 执行意义:
 * 初始化红黑树,包括初始化根节点、哨兵节点、ngx_rbtree_insert_pt节点添加方法
 */
#define ngx_rbtree_init(tree, s, i)                                           \
    ngx_rbtree_sentinel_init(s);                                              \
    (tree)->root = s;                                                         \
    (tree)->sentinel = s;                                                     \
    (tree)->insert = i

2.2.2 ngx_rbtree_insert: add nodes to the red-black tree

/*
 * 参数含义:
 * - tree:是红黑树容器的指针
 * - node:是需要添加到红黑树的节点指针
 * 
 * 执行意义:
 * 向红黑树中添加节点,该方法会通过旋转红黑树保持树的平衡.
 */
void ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{
    ngx_rbtree_node_t  **root, *temp, *sentinel;

    /* a binary tree insert */

    root     = &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 */

    /* 根据红黑树的性质调整树的结构 */
    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);
}

2.2.3 ngx_rbtree_insert_value: Insert method one

/*
 * 参数含义:
 * - temp:是红黑树容器的指针
 * - node:是待添加元素的ngx_rbtree_node_t成员的指针
 * - sentinel:是这棵红黑树初始化时哨兵节点的指针
 *
 * 执行意义:
 * 向红黑树添加数据节点,每个数据节点的关键字都是唯一的,不存在同一个
 * 关键字有多个节点的问题.
 */
void ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
    ngx_rbtree_node_t *sentinel)
{
    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        /* 首先比较 key 关键字,红黑树中以 key 作为第一索引关键字 */
        p = (node->key < temp->key) ? &temp->left : &temp->right;

        /* 如果当前节点是哨兵节点,则跳出循环准备插入节点 */
        if (*p == sentinel) 
        {
            break;
        }
        
        /* 若不是哨兵,则继续遍历下一个节点 */
        temp = *p;
    }

    /* 将 node 插入到该位置 */
    *p = node;
    node->parent = temp;
    /* 左右子节点都是哨兵节点 */
    node->left   = sentinel;
    node->right  = sentinel;
    /* 将节点颜色置为红色。注意,红黑树的ngx_rbtree_insert方法会在
     * 可能的旋转操作后重置该节点的颜色 */
    ngx_rbt_red(node);
}

2.2.4 ngx_rbtree_min: Find the smallest node in the current node and its subtrees

/*
 * 参数含义:
 * - node:是红黑树中ngx_rbtree_node_t类型的节点指针
 * - sentinel:是这棵红黑树的哨兵节点
 * 
 * 执行意义:
 * 找到当前节点及其子树中最小节点(按照key关键字)
 */
static ngx_inline ngx_rbtree_node_t *ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    while (node->left != sentinel)
    {
        node = node->left;
    }

    return node;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324972618&siteId=291194637