【C语言】红黑树详解以及C语言模拟

    • 一、红黑树的性质
    • 二、红黑树的旋转操作
    • 三、红黑树的插入操作
    • 四、红黑树的删除操作
    • 五、红黑树的应用
    • 六、C语言模拟红黑树
    • 七、总结

 红黑树是一种自平衡二叉查找树,它能够保持树的平衡,从而确保查找、插入和删除的最坏情况时间复杂度为O( l o g n log_n logn)。在红黑树中,节点被涂成红色或黑色,并且通过旋转和重新着色等操作来保持树的平衡。本文将详细介绍红黑树的原理,并使用C语言进行模拟实现。

一、红黑树的性质

请添加图片描述

红黑树具有以下性质:

  1. 每个节点非红即黑。
  2. 根节点是黑色的。
  3. 每个叶子节点(NIL)是黑色的。
  4. 如果一个节点是红色的,则它的子节点必须是黑色的(不能有两个连续的红色节点)。
  5. 从任一节点到其每个叶子节点的所有路径都包含相同数量的黑色节点。

二、红黑树的旋转操作

红黑树通过旋转操作来维护树的平衡。旋转分为左旋和右旋两种操作。

  1. 左旋:将某个节点作为旋转中心,将其右子节点向上移动,代替该节点的位置,然后将该节点作为左子节点。
  2. 右旋:将某个节点作为旋转中心,将其左子节点向上移动,代替该节点的位置,然后将该节点作为右子节点。

三、红黑树的插入操作

红黑树的插入操作包括以下几个步骤:

  1. 执行二叉查找树的插入操作,将新节点涂成红色。
  2. 调整红黑树,使其满足红黑树的性质。
    插入操作可能导致以下几种情况:
  3. 叔叔节点是红色:此时,只需重新着色即可。
  4. 叔叔节点是黑色或不存在:
    (1)当前节点是父节点的左子节点,且父节点是祖父节点的左子节点,或者当前节点是父节点的右子节点,且父节点是祖父节点的右子节点。此时,只需进行一次旋转操作。
    (2)当前节点是父节点的左子节点,且父节点是祖父节点的右子节点,或者当前节点是父节点的右子节点,且父节点是祖父节点的左子节点。此时,需要进行两次旋转操作。

四、红黑树的删除操作

红黑树的删除操作包括以下几个步骤:

  1. 执行二叉查找树的删除操作。
  2. 调整红黑树,使其满足红黑树的性质。
    删除操作可能导致以下几种情况:
  3. 被删除节点是红色:直接删除,不影响红黑树的性质。
  4. 被删除节点是黑色:
    (1)被删除节点的子节点是红色:直接删除,并将子节点涂成黑色。
    (2)被删除节点的子节点是黑色:需要进行调整,以保持红黑树的性质。

五、红黑树的应用

 红黑树在计算机科学中有着广泛的应用,例如在Java的TreeMap和TreeSet中,以及C++的STL中。红黑树能够提供高效的查找、插入和删除操作,因此在需要维护有序数据集合的场景中,红黑树是一个很好的选择。

六、C语言模拟红黑树

以下是一个简单的C语言实现红黑树的示例:

#include <stdio.h>
#include <stdlib.h>

typedef enum {RED, BLACK} Color;

typedef struct Node {
    int data;
    Color color;
    struct Node *parent;
    struct Node *left;
    struct Node *right;
} Node;

/**
 * 创建并初始化一个节点。
 * @param data 节点中要存储的数据。
 * @return 返回指向新创建节点的指针。
 */
Node *createNode(int data) {
    // 分配内存给新节点
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("Memory allocation failed!\n"); // 内存分配失败处理
        exit(1);
    }
    newNode->data = data; // 设置节点数据
    newNode->color = RED; // 新插入的节点默认为红色
    newNode->parent = NULL; // 初始化父节点为NULL
    newNode->left = NULL; // 初始化左子节点为NULL
    newNode->right = NULL; // 初始化右子节点为NULL
    return newNode; // 返回新创建的节点
}

/**
 * 左旋操作,用于平衡二叉搜索树(BST)。
 * 
 * @param root 指向当前树根节点的指针的地址。
 * @param x 需要进行左旋操作的节点。
 * 说明:函数不返回任何值,但会修改树的结构。
 */
void leftRotate(Node **root, Node *x) {
    Node *y = x->right;
    x->right = y->left;
    if (y->left != NULL)
        y->left->parent = x;
    y->parent = x->parent;
    if (x->parent == NULL)
        *root = y; // 如果x是根节点,更新根节点。
    else if (x == x->parent->left)
        x->parent->left = y; // 如果x是其父节点的左子节点,更新左子节点。
    else
        x->parent->right = y; // 如果x是其父节点的右子节点,更新右子节点。
    y->left = x;
    x->parent = y;
}

/**
 * 右旋操作
 * 
 * @param root 指向当前树根节点的指针的地址。
 * @param x 需要进行右旋操作的节点。
 * 说明:函数不返回任何值,但会修改树的结构。
 */
void rightRotate(Node **root, Node *y) {
    Node *x = y->left;
    y->left = x->right;
    if (x->right != NULL)
        x->right->parent = y;
    x->parent = y->parent;
    if (y->parent == NULL)
        *root = x;
    else if (y == y->parent->left)
        y->parent->left = x;
    else
        y->parent->right = x;
    x->right = y;
    y->parent = x;
}

/**
 * 插入修复函数
 * 该函数用于在红黑树中插入节点后,维护红黑树的性质。主要通过旋转和颜色的变换来保持红黑树的性质。
 * 
 * @param root 指向红黑树根节点的指针的地址
 * @param z 待插入的节点
 */
void insertFixup(Node **root, Node *z) {
    while (z != *root && z->parent->color == RED) {
        if (z->parent == z->parent->parent->left) {
            Node *y = z->parent->parent->right;
            if (y != NULL && y->color == RED) {
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;
                z = z->parent->parent;
            } else {
                if (z == z->parent->right) {
                    z = z->parent;
                    leftRotate(root, z);
                }
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rightRotate(root, z->parent->parent);
            }
        } else {
            Node *y = z->parent->parent->left;
            if (y != NULL && y->color == RED) {
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;
                z = z->parent->parent;
            } else {
                if (z == z->parent->left) {
                    z = z->parent;
                    rightRotate(root, z);
                }
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                leftRotate(root, z->parent->parent);
            }
        }
    }
    (*root)->color = BLACK;
}

/**
 * 向红黑树中插入一个新节点
 * 
 * @param root 指向红黑树根节点的指针的地址
 * @param data 需要插入的数据
 */
void insert(Node **root, int data) {
    Node *z = createNode(data);
    Node *y = NULL;
    Node *x = *root;
    while (x != NULL) {
        y = x;
        if (z->data < x->data)
            x = x->left;
        else
            x = x->right;
    }
    z->parent = y;
    if (y == NULL)
        *root = z;
    else if (z->data < y->data)
        y->left = z;
    else
        y->right = z;
    insertFixup(root, z);
}


void inorderTraversal(Node *root) {
    if (root != NULL) {
        inorderTraversal(root->left);
        printf("%d ", root->data);
        inorderTraversal(root->right);
    }
}

int main() {
    Node *root = NULL;
    insert(&root, 10);
    insert(&root, 20);
    insert(&root, 30);
    insert(&root, 15);
    insert(&root, 18);

    inorderTraversal(root);
    printf("\n");

    return 0;
}

完整代码(包括红黑树节点输出以及删除节点操作)【链接】

结果
在这里插入图片描述

七、总结

 红黑树是一种自平衡二叉查找树,它能够保持树的平衡,从而保证查找、插入和删除的最坏情况时间复杂度为O( l o g n log_n logn)。红黑树通过旋转操作和颜色变更来维护树的性质,适用于需要维护有序数据集合的场景。在Java和C++的STL中,红黑树得到了广泛的应用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软考-系统集成项目管理中级--项目人力资源管理(输入输出很重要!!!本章可能包含案例题)

本章历年考题分值统计 本章重点常考知识点汇总清单(学握部分可直接理解记忆) 10、项目沟通管理计划一般应包括以下内容:(掌握)12上59&#xff0c;10下59&#xff0c;13上53&#xff0c;13上57,14下58&#xff0c;15下58&#xff0c;16上59 考题 (1)干系人的沟通需求。 (2)针对沟…

爬虫抓取网站数据

Fiddler 配置fiddler工具结合浏览器插件 配置fiddler Tools--Options 抓包技巧 谷歌浏览器开启无痕浏览,使用SwitchyOmega配置好代理端口 Ctrl x 清理所有请求记录,可以删除指定不需要日志方便观察 设置按请求顺序 观察cookie,观察请求hesder cookie和row返回结果 Swit…

C++/QT + Mysql + Tcp 企业协作管理系统

目录 一、项目介绍 二、项目展示 三、源码获取 一、项目介绍 1、项目概要&#xff1a;C/S架构、数据库Mysql、C、QT&#xff1b;支持实时通信、局域网内通信&#xff0c;可多个客户端同时登录&#xff1b; 2、&#xff08;Server&#xff09;管理端&#xff1a;用户管理、…

科技云报道:AIGC掀算力需求革命,边缘计算将不再“边缘”

科技云报道原创。 随着以大模型为代表的AIGC时代拉开序幕&#xff0c;算力需求持续爆发&#xff0c;AI与边缘深度融合已是大势所趋&#xff0c;越来越多的企业开始积极布局GenAI。 GenAI技术的商用化部署和应用成为企业竞逐的新阵地&#xff0c;勾勒出大模型从“技术力”转向…

数组模拟几种基本的数据结构

文章目录 数组模拟单链表数组模拟双链表数组实现栈数组模拟队列总结 数组模拟单链表 首先类比结构体存储单链表&#xff0c;我们需要一个存放下一个节点下标的数组&#xff0c;还需要一个存储当前节点的值的数组&#xff0c;其次就是一个int类型的索引&#xff0c;这个索引指向…

挑战一周完成Vue3实战项目硅谷甄选Day1:项目初始化、项目配置、项目集成

一、项目初始化 node v16.4.0以上&#xff08;查看node版本 : node -v&#xff09; pnpm 8.0.0&#xff08;npm i -g pnpm8.0.0&#xff09; 在想创建的位置新建文件夹自己命名 在此文件夹下cmd:pnpm create vite 选择如下配置 Project name&#xff08;项目名称&#xff0…

Java设计模式 _创建者模式_工厂模式(普通工厂和抽象工厂)

一、工厂模式 属于Java设计模式创建者模式的一种。在创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通过使用一个共同的接口来指向新创建的对象。 二、代码示例 场景&#xff1a;花店有不同的花&#xff0c;通过工厂模式来获取花。 1、普通工厂模式 逻辑步骤&#…

Spring - 5 ( 8000 字 Spring 入门级教程 )

一&#xff1a;Spring IoC&DI 1.1 方法注解 Bean 类注解是添加到某个类上的&#xff0c; 但是存在两个问题: 使用外部包里的类, 没办法添加类注解⼀个类, 需要多个对象, ⽐如多个数据源 这种场景, 我们就需要使用方法注解 Bean 我们先来看方法注解如何使用: public c…

AI-数学-高中-42导数的概念与意义

原作者视频&#xff1a;【导数】【一数辞典】1导数的概念与意义_哔哩哔哩_bilibili .a是加速度&#xff1b;

OpenAI 笔记:获取embedding

1 输入openai的api key from openai import OpenAIclient OpenAI(api_key**) 2 举例 response client.embeddings.create(input"Hello",model"text-embedding-3-small" )print(response.data[0].embedding) 默认情况下&#xff0c;text-embedding-3-s…

配置Linux【虚拟机】与 windows【宿主机】网络互通 (面向小白,简单操作)

1. 启动虚拟机&#xff0c;运行Linux系统 这里我使用 VMware Workstation Pro 来运行Linux系统&#xff08;cent-os7&#xff09;2. 鼠标右键打开终端 3. 输入 cd /etc/sysconfig/network-scripts , 然后输入ls &#xff0c;查看当前目录下的网卡 一般来说&#xff0c;虚拟机的…

计算机网络基础认识

本篇文章是我在B站上看到关于计算机网络的介绍视频收到的启发。本篇文章的内容来自【网络】半小时看懂<计算机网络>_哔哩哔哩_bilibili 一、物理层 从常理来说&#xff0c;进行连个设备之间的通讯&#xff0c;首先最容易想到的就是使用一根线连接两个设备进行通讯。但是…

Docker的数据管理、网络通信和dockerfile

目录 一、Docker的数据管理 1. 数据卷 1.1 数据卷定义 1.2 数据卷配置 2. 数据卷容器 2.1 创建数据卷容器 2.2 使用--volume-from来挂载test1 二、端口映射 三、容器互联 1. 创建容器互联 ​编辑2. 进入test2测试&#xff08;ping 容器名/别名&#xff09; 四、Dock…

【弱监督点云分割】All Points Matter:用于弱监督三维分割的熵细化分布对齐

All Points Matter: Entropy-Regularized Distribution Alignment for Weakly-supervised 3D Segmentation 摘要&#xff1a; 伪标签被广泛应用于弱监督三维分割任务中&#xff0c;在这种任务中&#xff0c;只有稀疏的地面真实标签可供学习使用。现有方法通常依赖经验标签选择…

用立创EDA实现一个小项目

项目介绍 名称&#xff1a;蓝牙音响 功能&#xff1a;按键切换 蓝牙控制 语音控制 项目流程 市场调研产品立项----老板--经理硬件&#xff08;外观、尺寸、大小、使用环境&#xff09;软件&#xff08;代码开发环节&#xff09;产品测试 以管理员身份运行 新建文件夹&…

Python学习从0开始——项目一day02数据库连接

Python学习从0开始——项目一day02数据库连接 一、在线云数据库二、测试数据库连接三、数据库驱动介绍四、SQL执行4.1插入测试数据4.2安装数据库连接模块4.3测试SQL语句执行4.4执行SQL的固定步骤及示例 一、在线云数据库 找了一个在线数据库&#xff0c;需要邮箱注册&#xff…

使用Docker搭建Redis主从集群

文章目录 ☃️前言☃️搭建❄️❄️架构❄️❄️实例说明❄️❄️搭建第一个服务器上的两个实例❄️❄️搭建第二个服务器上的一个实例 ☃️开启主从❄️❄️改配置❄️❄️重启从节点 ☃️验证 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 …

《MATLAB科研绘图与学术图表绘制从入门到精通》示例:绘制婴儿性别比例饼图

在MATLAB 中可以使用 pie 函数来创建饼图。饼图是一种展示不同部分占总体的相对比例的图表。 本示例从“婴儿出生数据.csv”文件读取婴儿出生数据&#xff0c;然后计算男性和女性婴儿的数量&#xff0c;使用MATLAB绘制饼图。 配套图书链接&#xff1a;https://item.jd.com…

【ruoyi-vue】axios的封装理解和基本使用

axios的配置 ruoyi的前端对axios进行了封装&#xff0c;让我们发get请求或者是post请求更加方便了。 ruoyi对axios的封装在下面文件中&#xff1a;打开文件&#xff0c;可以看到它有三个显眼的方法&#xff0c;分别是request拦截器、response拦截器和通用下载方法。ruoYi接口地…

MySQL主从的应用

说明&#xff1a;本文介绍MySQL主从在实际中的应用。主从搭建和问题参考下面两篇文章&#xff1a; MySQL主从结构搭建 搭建MySQL主从结构时的问题 数据迁移 当我们搭建完MySQL主从&#xff0c;第一步当然是把历史数据导入到主从结构中。有以下两种方式&#xff1a; 开启主从…