红黑树的插入(NGINX源码)

news2024/11/15 9:47:36

下载并查看NGINX源码

访问NGINX下载页面,找到所需版本

https://nginx.org/en/download.html

使用wget下载源码包,替换版本号为所需版本

wget http://nginx.org/download/nginx-1.24.0.tar.gz

解压源码包

tar -xzvf nginx-1.24.0.tar.gz

 进入解压后的目录

cd nginx-1.24.0

红黑树定义

红黑树是每个节点都带有颜色属性的二叉查找树。它是一种自平衡的二叉查找树,能够在插入和删除数据时通过特定操作保持二叉查找树的平衡性,从而获得较高的查找性能。除了二叉查找树的强制要求外,任何一棵有效的红黑树还必须满足以下性质:

  1. 节点颜色:每个节点要么是红色,要么是黑色。
  2. 根节点:根节点必须是黑色。
  3. 叶节点:所有叶节点都是黑色。
  4. 红色节点的子节点:红色节点的子节点必须是黑色(即不能有两个连续的红色节点)。
  5. 每个节点的黑高:从任何节点到其所有后代叶节点的路径上,必须包含相同数量的黑色节点。

红黑树数据结构 

红黑树节点结构体

存储了节点间的对应关系和节点的真实数据。

typedef ngx_uint_t  ngx_rbtree_key_t; // 方便更换key的属性
typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;

struct ngx_rbtree_node_s {
    ngx_rbtree_key_t       key;    // 节点的键值,类型为ngx_rbtree_key_t
    ngx_rbtree_node_t     *left;
    ngx_rbtree_node_t     *right;
    ngx_rbtree_node_t     *parent;
    u_char                 color;
    u_char                 data;    // 节点的数据,类型为u_char,通常存储附加的信息或标记
};

红黑树结构体

维护整个红黑树的根节点及定义插入的节点时执行的函数指针。

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;
    ngx_rbtree_node_t     *sentinel;
    ngx_rbtree_insert_pt   insert;
};

红黑树插入节点node

给节点染色

#define ngx_rbt_red(node)               ((node)->color = 1)
#define ngx_rbt_black(node)             ((node)->color = 0)
#define ngx_rbt_is_red(node)            ((node)->color)
#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))
#define ngx_rbt_copy_color(n1, n2)      (n1->color = n2->color)

以node为支点右旋

示意图

代码 
// 以node为支点右旋
static ngx_inline void ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
    ngx_rbtree_node_t *node) {

    // 1 标记node的左孩子
    ngx_rbtree_node_t  *temp;
    temp = node->left; 
    
    // 2 改变node的左指针及其左孩子的父指针
    //   如果其左孩子是叶节点,不用将叶节点的父指针指向node
    node->left = temp->right;
    if (temp->right != sentinel) {
        temp->right->parent = node;
    }
    
    // 3 改变temp的父指针,及其可能存在的父节点的孩子指针
    temp->parent = node->parent;
    if (node == *root) {
        *root = temp;
    } else if (node == node->parent->right) {
        node->parent->right = temp;
    } else {
        node->parent->left = temp;
    }

    // 4 改变temp的右指针及其右孩子的父指针
    temp->right = node;
    node->parent = temp;
}

 以node为支点左旋

示意图

代码 
static ngx_inline void ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
    ngx_rbtree_node_t *node) {
    
    // 1 标记node的右孩子
    ngx_rbtree_node_t  *temp;
    temp = node->right;

    // 2 改变node的右指针及其右孩子的父指针
    //   如果其右孩子是叶节点,不用将叶节点的父指针指向node
    node->right = temp->left;
    if (temp->left != sentinel) {
        temp->left->parent = node;
    }

    // 3 改变temp的父指针,及其可能存在的父节点的孩子指针
    temp->parent = node->parent;
    if (node == *root) {
        *root = temp;
    } else if (node == node->parent->left) {
        node->parent->left = temp;
    } else {
        node->parent->right = temp;
    }

    // 4 改变temp的左指针及其左孩子的父指针
    temp->left = node;
    node->parent = temp;
}

插入操作

流程图

代码
void ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node) {

    ngx_rbtree_node_t  **root, *temp, *sentinel;
    root = &tree->root;    // 获取树的根节点地址,以便进行旋转操作
    sentinel = tree->sentinel;

    // 如果树为空,直接将node节点作为根节点
    if (*root == sentinel) {
        node->parent = NULL;
        node->left = sentinel;
        node->right = sentinel;
        ngx_rbt_black(node);  // 将新节点颜色设为黑色
        *root = node;
        return;
    }

    // node不是根节点不为空,调用插入函数将节点插入到正确位置
    tree->insert(*root, node, sentinel);

    // 当新节点不是根节点且其父节点为红色时,需要进行平衡调整
    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);
}
叔叔节点是红色

4种情况,只列出两种。注意,这里的黑色矩形不是叶子节点,而是树。

祖父节点到插入节点是LL型

祖父节点到插入节点是LR型

祖父节点到插入节点是RR型

祖父节点到插入节点是RL型

推荐一下

https://github.com/0voice

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

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

相关文章

Java的输入输出

秋招笔试很多都是要自己写输出输出的,所以对常见的整理一下,后续也会持续更新的~~~ 目录 1.java中的Scanner类 1.1next()方法和nextLine()方法的区别 1. next() 方法 示例 2. nextLine() 方法 示例 主要区别 使用场景 2.print类 3.常用的转换…

音频左右声道数据传输_2024年9月6日

如下为音频数据传输标准I2S总线的基本时序图 I2S slave将I2S master发送来的左右声道的串行数据DATA转变为16bit的并行数据 WS为左右声道选择信号,WS高代表左声道,WS低代表右声道; WS为高和为低都持续18个周期,前面16个周期用来传输数据。 I2…

npm安装时候报错certificate has expired

打开了一个很久没用的电脑,npm和node都装好了,安装包的时候一直报错 request to https://registry.npm.taobao.org/create-react-app failed, reason: certificate has expired而且先报错rollbackFailedOptional 然而npm没什么问题,是ssl过…

【数据结构与算法 | 灵神题单 | 自底向上DFS篇】力扣965, 2331, 100, 1379

1. 力扣965:单值二叉树 1.1 题目: 如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时,才返回 true;否则返回 false。 示例 1: 输入:[1,1,1,1,1,n…

UE5学习笔记22-武器瞄准和武器自动开火

0、一些疑问的记录 1.UUserWidget类和AHUD类的区别。两者都是关于界面显示的类。 实践: 想让界面和用户有交互使用UUserWidget,如果不要交互只是显示使用AHUD类,例如使用UUserWidget类制作开始界面,游戏开始,游戏设置&…

TensorRT-LLM——优化大型语言模型推理以实现最大性能的综合指南

引言 随着对大型语言模型 (LLM) 的需求不断增长,确保快速、高效和可扩展的推理变得比以往任何时候都更加重要。NVIDIA 的 TensorRT-LLM 通过提供一套专为 LLM 推理设计的强大工具和优化,TensorRT-LLM 可以应对这一挑战。TensorRT-LLM 提供了一系列令人印…

AD的入门操作

锦囊 1、打开AD后,一般默认打开上一个工程,这个时候如果想要打开新的工程,那就必须要创建一个项目,然后再在项目中添加原理图库和PCB库。 2、大多数情况下,直接使用库,不用自己再画原理图和封装库。 3、…

LeetCode[中等] 49.字母异位词分组

给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 思路&#xff1a; new Dictionary<string, List<string>>() 存储数据&#xff0c;key为排序之后的字符…

超高速传输 -- 相干光通信和非相干光通信

概述&#xff1a;技术对比 项目非相干光通信相干通信定义不需要相干本振光的光传输系统。采用本振光进行相干检测的光传输系统。调制解调技术发送端&#xff1a;强度调制。 接收端&#xff1a;直接检测。发送端&#xff1a;外调制。 接收端&#xff1a;本振光相干检测。码型幅…

完美的宝塔面板防御策略,基于 fail2ban

之前分享过宝塔面板配合fail2ban&#xff0c;以及开启cloudflare的cdn双重防御的教程&#xff0c;并制作了便捷的脚本这次不靠cloudflare的减速cdn&#xff0c;看看防御效果怎么样 系统环境 debian/ubuntu nginx 宝塔面板 注意要点 1.在宝塔面板开启站点日志 2.添加服务器白名…

加密与安全_HTTPS TLS 1.2 连接(RSA 握手)的整个过程解读

文章目录 HTTPS 数据传输的安全性保障SSL/TLS 作为混合加密系统的典范HTTPS TLS 1.2 连接&#xff08;RSA 握手&#xff09;的整个过程TLS 握手过程解析1. TCP 三次握手 (最顶部的黄色部分)2. TLS 握手阶段 (红色部分)2.1 Client Hello2.2 Server Hello2.3 CA 证书验证2.4 Clie…

[Python数据可视化]Plotly Express: 地图数据可视化的魅力

在数据分析和可视化的世界中&#xff0c;地图数据可视化是一个强大而直观的工具&#xff0c;它可以帮助我们更好地理解和解释地理数据。Python 的 Plotly Express 库提供了一个简单而强大的方式来创建各种地图。本文将通过一个简单的示例&#xff0c;展示如何使用 Plotly Expre…

【JavaWeb】利用IDEA2024+tomcat10配置web6.0版本搭建JavaWeb开发项目

之前写过一篇文章&#xff1a;《【JavaWeb】利用IntelliJ IDEA 2024.1.4 Tomcat10 搭建Java Web项目开发环境&#xff08;图文超详细&#xff09;》详细讲解了如何搭建JavaWeb项目的开发环境&#xff0c;里面默认使用的Web版本是4.0版本的。但在某些时候tomcat10可能无法运行we…

24年蓝桥杯及攻防世界赛题-MISC-1

2 What-is-this AZADI TOWER 3 Avatar 题目 一个恐怖份子上传了这张照片到社交网络。里面藏了什么信息&#xff1f;隐藏内容即flag 解题 ┌──(holyeyes㉿kali2023)-[~/Misc/tool-misc/outguess] └─$ outguess -r 035bfaa85410429495786d8ea6ecd296.jpg flag1.txt Rea…

iKuai使用及设置流程

iKuai使用及设置流程 iKuai安装步骤 一、配置主机 1.电脑连接ETH0网口 2.ETH1网口连接猫上面的千兆口 3.手动配置pc的IP地址和192.168.1.1./24在同一网段 3.浏览器输入192.168.1.1 admin admin 二、外网设置 1.直接联通电信网络设置 2.点击 网络设置-内外网设置-点击接…

LeetCode从入门到超凡(一)枚举算法

前言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的LeetCode学习总结文档&#xff1b;本文主要讲解枚举算法。&#x1f495;&#x1f495;&#x1f60a; 一、基本概念 1.定义 定义&am…

记录小数点

记录data frame小数点后面省略掉0的问题 iloc得到的series .to_list() 0被省略掉 to_list() 可能会将浮点数转换为默认格式。先将数据转换为字符串以保留格式 df_2708.iloc[2,:].apply(lambda x: f{x:.3f}).to_list()自定义保留小数点后几位 def formatter(value):return &q…

【工作流集成】springboot+vue工作流审批系统(实际源码)

前言 activiti工作流引擎项目&#xff0c;企业erp、oa、hr、crm等企事业办公系统轻松落地&#xff0c;一套完整并且实际运用在多套项目中的案例&#xff0c;满足日常业务流程审批需求。 一、项目形式 springbootvueactiviti集成了activiti在线编辑器&#xff0c;流行的前后端…

Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 垃圾回收算法

文章目录 垃圾回收机制垃圾判断算法引用计数法可达性分析算法虚拟机栈中的引用&#xff08;方法的参数、局部变量等&#xff09;本地方法栈中 JNI 的引用类静态变量运行时常量池中的常量 垃圾收集算法Mark-Sweep&#xff08;标记-清除&#xff09;算法Copying&#xff08;标记-…

电脑右击没有txt文件

文本文档是一个好工具&#xff0c;小而快&#xff0c;比word快多&#xff0c;一般情况下&#xff0c;记录都会先用txt文本&#xff0c;但是今天发现右击菜单新建里面没有&#xff0c;怎么回事&#xff1f; 这个需要打开注册编辑表修改 一、打开注册编辑表 win R 输入regedi…