Nginx数据结构之红黑树ngx_rbtree_t
1. 什么是红黑树?
1.1 概述
红黑树实际上是一种自平衡二叉查找树。
二叉树是什么?二叉树是每个节点最多有两个子树的树结构,每个节点都可以用于存储数据,可以由任 1 个节点访问它的左右
子树或父节点。
二叉查找树是什么?二叉查找树或者是一棵空树,是具有下列性质的二叉树。
- 每个节点都有一个作为查找依据的关键码(key),所有节点的关键码互不相同。
- 左子树(如果存在)上所有节点的关键码都小于根节点的关键码。
- 右子树(如果存在)上所有节点的关键码都大于根节点的关键码。
- 左子树和右子树也是二叉查找树。
这样,一棵二叉查找树的所有元素节点都是有序的。在二叉树的形态比较平衡的情况下,它的检索效率很高,有些类似与二分
法检索有序数组的效率。一般情况下,查询复杂度是与目标节点到根节点的距离(即深度)有关的。然而,不断地添加、删除
节点,可能造成二叉查找树形态非常不平衡,在极端情形下它会变成单链表,检索效率也就会变得低下。
自平衡二叉查找树是什么?在不断地向二叉查找树中添加、删除节点时,二叉查找树自身通过形态的变换,始终保持着一定程
度上的平衡,即为自平衡二叉查找树。红黑树是一种自平衡性较好的二叉查找树。
ngx_rbtree_t 红黑树容器中的元素都是有序的,它支持快速的检索、插入、删除操作,也支持范围查询、遍历等操作。
1.2 红黑树特性
红黑树是指每个节点都带有颜色属性的二叉查找树,其中颜色为红色或黑色。除了二叉查找树的一般要求以外,对于红黑树
还有如下的特性:
- 特性1:节点是红色或黑色。
- 特性2:根节点是黑色。
- 特性3:所有叶子节点都是黑色(叶子是 NIL 节点,也叫 “哨兵”)。
- 特性4:每个红色节点的两个子节点都是黑色(每个叶子节点到根节点的所有路径上不能有两个连续的红色节点)。
- 特性5:从任一节点到每个叶子节点的所有简单路径都包含相同数目的黑色节点。
这些约束加强了红黑树的关键性质:从根节点到叶子节点的最长可能路径长度不大于最短可能路径的两倍,这样这个树大致
上就是平衡了。
示例:依次往空的 ngx_rbtree_t 红黑树容器中添加元素 1、6、8、11、13、15、17、22、25、27 后,会形成如下的红黑树:
2. ngx_rbtree_t 红黑树的实现
2.1 相关结构体
2.1.1 ngx_rbtree_node_t:红黑树的节点结构体
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 是红黑树实现中必须用到的数据结构,一般把它放到结构体中的第 1 个成员中,这样方便把自定义的结
构体强制成 ngx_rbtree_node_t 类型。
ngx_rbtree_node_t 结构体中的 key 成员是每个红黑树节点的关键字,它必须是整型。红黑树的排序主要依据 key 成员
(自定义 ngx_rbtree_insert_pt 方法后,节点的其他成员也可以在 key 排序的基础上影响红黑树的形态)。
2.1.2 ngx_rbtree_t:红黑树容器
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;
};
红黑树是一个通用的数据结构,它的节点(或者称为容器的元素)可以是包含基本红黑树节点的任意结构体。对于不同的结
构体,很多场合下是允许不同的节点拥有相同的关键字的。例如,不同的字符串可能会散列出相同的关键字,这时它们在红
黑树中的关键字是相同的,然而它们又是不同的节点,这样在添加时就不可以覆盖原有同名关键字的节点,而是作为新插入
的节点存在。因此,将添加元素的方法抽象出 ngx_rbtree_insert_pt 函数指针可以很好地实现这一思想。
2.2 提供的接口
2.2.1 ngx_rbtree_init:初始化一棵红黑树
/* 设置该节点颜色为红色 */
#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: 向红黑树中添加节点
/*
* 参数含义:
* - 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: 插入方法一
/*
* 参数含义:
* - 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: 找到当前节点及其子树中最小节点
/*
* 参数含义:
* - 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;
}
Nginx数据结构之红黑树ngx_rbtree_t的更多相关文章
- 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t
新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...
- 菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]
菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
- 物联网安全himqtt防火墙数据结构之红黑树源码分析
物联网安全himqtt防火墙数据结构之红黑树源码分析 随着5G的发展,物联网安全显得特别重要,himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWa ...
- Linux 内核里的数据结构:红黑树(rb-tree)
转自:https://www.cnblogs.com/slgkaifa/p/6780299.html 作为一种数据结构.红黑树可谓不算朴素.由于各种宣传让它过于神奇,网上搜罗了一大堆的关于红黑树的文章 ...
- 【数据结构】红黑树与跳表-(SortSet)-(TreeMap)-(TreeSet)
SortSet 有序的Set,其实在Java中TreeSet是SortSet的唯一实现类,内部通过TreeMap实现的:而TreeMap是通过红黑树实现的:而在Redis中是通过跳表实现的: Skip ...
- JDK1.8的HashMap数据结构及红黑树
在JDK1.6,1.7中,HashMap的实现都是用基础的“拉链法”去实现,即数组+链表的形式.如下图:通过不同的hash值,来对数据进行分配存储. 关于HashMap的Entry长度,可以参考htt ...
- 【数据结构】红黑树 C语言代码
连看带写花了三天,中途被指针引用搞得晕晕乎乎的. 插入和删除的调整过程没有看原理,只看了方法,直接照着写的. 看了两份资料,一份是算法导论第12-13章, 另一份是网上的资料http://blog.c ...
- D&F学数据结构系列——红黑树
红黑树 定义:一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树: 1)每个结点不是红的就是黑的 2)根结点是黑的 3)每个叶结点是黑的 4)如果一个结点是红的,它的两个儿子都是黑的(即不可能有两个 ...
- 【数据结构】红黑树-Java实现
WIKI:https://en.wikipedia.org/wiki/Red%E2%80%93black_tree 转:红黑树(五)之 Java的实现 总结的比较精炼的: http://www.cnb ...
随机推荐
- jquery.validate.js表单验证 jquery.validate.js的用法
jquery.validate.js这个插件已经用了2年多了,是一个不可多得的表单验证最方便快捷的插件.基于jquery的小插件,基本小白一学就会上手,对于项目表单页面比较多,元素比较多的校验,该插件 ...
- CSS模块化:less
less的安装与基本使用 less的语法及特性 一.本地使用less的方法 Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言.是一种动态样式语言,属于c ...
- Linux--环境变量配置文件
Linux系统中环境变量配置文件分为两类: 全局环境变量配置文件 /etc/profile 用户环境变量配置文件 ~/.bash_profile . ~/.bash_login ~/.profile ...
- hbase shell 基本操作
hbase shell 基本操作 启动HBASE [hadoop@master ~]$hbase shell 2019-01-24 13:53:59,990 WARN [main] ut ...
- 二、CentOS 7安装部署GitLab服务器(解决邮箱发信问题)
一.环境安装(10.0.0) 1.安装依赖软件 yum -y install policycoreutils policycoreutils-python openssh-server openssh ...
- 微信小程序开发(一)创建一个小程序Hello World!
开发微信小程序并不是很难,网上有很多小程序开发资料,尤其是微信官方的<小程序开发指南>最详细. 下面是我开发小程序的历程: 第一步,请前往https://mp.weixin.qq.com/ ...
- Rectangle Puzzle CodeForces - 281C (几何)
You are given two rectangles on a plane. The centers of both rectangles are located in the origin of ...
- Matrix Factorization in RecSys
矩阵分解在推荐系统中的应用. 参考链接:知乎. 传统SVD,Funk-SVD,Bias-SVD,SVD++. SVD奇异值分解及其意义. 漫谈奇异值分解.
- OpenGL相关文章
OpenGL之glMatrixMode函数的用法 gluOrtho2D和glViewport的作用&窗口与显示的关系 glViewport函数用法 纹理映射
- TETP服务和PXE功能
PXE PXE:Preboot Excution Environment, Intel公司研发,没有任何操作系统的主机,能够基于网络完成系统的安装工作.