红黑树原理及C语言实现

目录

一、原理

二、操作示例

三、应用场景

四、C语言实现红黑树

五、代码说明

六、红黑树和AVL树对比


一、原理

熟悉红黑树之前,我们需要了解二叉树与二叉查找树概念,参见前述相关文章:二叉查找树BST详解及其C语言实现-CSDN博客

红黑树是一种自平衡的二叉查找树,满足以下5个核心性质:

  1. 每个节点是红色或黑色

  2. 根节点是黑色

  3. 所有叶子节点(NIL节点)是黑色

  4. 红色节点的子节点必须是黑色

  5. 从任一节点到其叶子节点的所有路径包含相同数量的黑色节点

红黑树示例图

二、操作示例

 以插入序列[3, 21, 32, 15]为例:

插入3(根节点):3(B)插入21:3(B)\21(R)插入32(需旋转):3(B)              21(B)\      右旋       / \21(R)   →    3(R) 32(R)\32(R)插入15:21(B)/   \3(R)  32(B)\15(R)
(符合红黑树性质,无需调整)

三、应用场景

  1. C++ STL map/multimap、set/multiset

  2. Java TreeMap/TreeSet

  3. Linux内核进程调度

  4. 文件系统目录结构

  5. 实时计算系统

  6. 内存管理中的虚拟地址空间管理

四、C语言实现红黑树

#include <stdio.h>
#include <stdlib.h>typedef enum { RED, BLACK } Color;typedef struct Node {int key;Color color;struct Node *left, *right, *parent;
} Node;Node *NIL_LEAF; // 哨兵节点Node* create_node(int key) {Node* node = (Node*)malloc(sizeof(Node));node->key = key;node->color = RED;node->left = node->right = NIL_LEAF;node->parent = NIL_LEAF;return node;
}void left_rotate(Node **root, Node *x) {Node *y = x->right;x->right = y->left;if (y->left != NIL_LEAF)y->left->parent = x;y->parent = x->parent;if (x->parent == NIL_LEAF)*root = y;else if (x == x->parent->left)x->parent->left = y;elsex->parent->right = y;y->left = x;x->parent = y;
}void right_rotate(Node **root, Node *y) {Node *x = y->left;y->left = x->right;if (x->right != NIL_LEAF)x->right->parent = y;x->parent = y->parent;if (y->parent == NIL_LEAF)*root = x;else if (y == y->parent->left)y->parent->left = x;elsey->parent->right = x;x->right = y;y->parent = x;
}void fix_insert(Node **root, Node *z) {while (z->parent->color == RED) {if (z->parent == z->parent->parent->left) {Node *y = z->parent->parent->right;if (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;left_rotate(root, z);}z->parent->color = BLACK;z->parent->parent->color = RED;right_rotate(root, z->parent->parent);}} else {// 对称处理右子树情况}if (z == *root) break;}(*root)->color = BLACK;
}void insert(Node **root, int key) {Node *z = create_node(key);Node *y = NIL_LEAF;Node *x = *root;while (x != NIL_LEAF) {y = x;if (z->key < x->key)x = x->left;elsex = x->right;}z->parent = y;if (y == NIL_LEAF)*root = z;else if (z->key < y->key)y->left = z;elsey->right = z;fix_insert(root, z);
}// 中序遍历验证
void inorder(Node *node) {if (node != NIL_LEAF) {inorder(node->left);printf("%d(%s) ", node->key, node->color == RED ? "R" : "B");inorder(node->right);}
}int main() {// 初始化NIL节点NIL_LEAF = (Node*)malloc(sizeof(Node));NIL_LEAF->color = BLACK;NIL_LEAF->left = NIL_LEAF->right = NIL_LEAF->parent = NULL;Node *root = NIL_LEAF;insert(&root, 3);insert(&root, 21);insert(&root, 32);insert(&root, 15);printf("Inorder traversal: ");inorder(root);printf("\nRoot: %d(%s)\n", root->key, root->color == RED ? "R" : "B");return 0;
}

五、代码说明

  1. 使用NIL节点作为统一的叶子节点

  2. 实现左旋/右旋操作维护平衡

  3. 插入修复处理三种情况:

    • 叔节点为红色

    • 叔节点为黑且当前节点为右子

    • 叔节点为黑且当前节点为左子

  4. 通过颜色翻转和旋转操作保证红黑树性质

输出结果示例:

Inorder traversal: 3(R) 15(R) 21(B) 32(R) 
Root: 21(B)

该实现可用于构建高性能字典结构,时间复杂度保持在O(log n)级别,适用于需要频繁插入删除的场景。实际工程中可扩展支持泛型、删除操作和内存回收等功能。

六、红黑树和AVL树对比

以下是详细对比数据结构中AVL树和红黑树的优缺点及其适合使用的应用场景:

AVL树红黑树
平衡性严格平衡,左右子树高度差不超过1弱平衡,黑色节点路径长度一致,红色节点不连续
时间复杂度查找、插入、删除均为O(log n)查找、插入、删除在最坏情况下为O(log n)
优点

1. 查找效率高,因为树的高度始终保持较低

2. 适用于需要频繁查找但插入和删除次数较少的场景

1. 插入和删除操作更灵活,需要较少的旋转

2. 内存占用相对较低,因为节点不需要额外的平衡因子

缺点

1. 插入和删除操作可能需要频繁的旋转来维持平衡,增加了代码复杂度和运行开销

2. 节点需要额外维护平衡因子,增加了空间复杂度

1. 平衡性较弱,可能导致某些操作效率不如AVL树

2. 实现相对复杂,需要处理颜色和旋转规则

应用场景

1. 搜索操作频繁、插入和删除操作相对较少的场景

2. 对树的平衡性要求较高的场景,如某些数据库索引

1. 插入和删除操作较频繁、搜索操作相对较少的场景

2. 内存受限或需要高效迭代访问的场景,如内存管理、文件系统等

总结:

  • AVL树在保持严格平衡方面表现出色,适用于需要频繁查找且插入和删除次数较少的场景。然而,由于需要频繁的旋转操作和额外的平衡因子,其代码复杂度和空间复杂度相对较高。
  • 红黑树通过牺牲部分平衡性来换取更高的插入和删除效率,适用于插入和删除操作较频繁且搜索操作相对较少的场景。其内存占用相对较低,实现也相对简单,但需要处理颜色和旋转规则。

在选择使用哪种树结构时,需要根据具体的应用需求和性能要求来进行权衡。

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

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

相关文章

DeepSeek V2报告阅读

概况 MoE架构&#xff0c;236B参数&#xff0c;每个token激活参数21B&#xff0c;支持128K上下文。采用了包括多头潜在注意力&#xff08;MLA&#xff09;和DeepSeekMoE在内的创新架构。MLA通过将KV缓存显著压缩成潜在向量来保证高效的推理&#xff0c;而DeepSeekMoE通过稀疏计…

TCP服务器与客户端搭建

一、思维导图 二、给代码添加链表 【server.c】 #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <fcntl.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.…

【自动化测试】使用Python selenium类库模拟手人工操作网页

使用Python selenium类库模拟手人工操作网页 背景准备工作安装Python版本安装selenium类库下载selenium驱动配置本地环境变量 自动化脚本输出页面表单自动化填充相关代码 背景 待操作网页必须使用IE浏览器登录访问用户本地只有edge浏览器&#xff0c;通过edge浏览器IE模式访问…

如何通过Davinci Configurator来新增一个BswM仲裁规则

本文框架 前言1.增加一个Mode Declaration Group2.增加一个Mode Request RPorts3.与操作Port的SWC连线4.新建一个Expression5.新建ActionList6.将表达式新建或加进现有Rule内7.生成BswM及Rte模块代码8.在代码中调用RTE接口前言 在Autosar模式管理系列介绍01-BswM文章中,我们对…

智慧交通:如何通过数据可视化提升城市交通效率

随着城市化进程的加速&#xff0c;交通管理面临着前所未有的挑战。为了应对日益复杂的交通状况&#xff0c;智慧交通系统应运而生&#xff0c;其中数据可视化技术成为了提升交通管理效率的关键一环。本文将探讨如何利用山海鲸可视化软件来优化交通管理&#xff0c;并展示其在智…

Android Studio:如何利用Application操作全局变量

目录 一、全局变量是什么 二、如何把输入的信息存储到全局变量 2.1 MainApplication类 2.2 XML文件 三、全局变量读取 四、修改manifest ​编辑 五、效果展示 一、全局变量是什么 全局变量是指在程序的整个生命周期内都可访问的变量&#xff0c;它的作用范围不限于某个…

DeepSeek模型构建与训练

在完成数据预处理之后,下一步就是构建和训练深度学习模型。DeepSeek提供了简洁而强大的API,使得模型构建和训练变得非常直观。无论是简单的全连接网络,还是复杂的卷积神经网络(CNN)或循环神经网络(RNN),DeepSeek都能轻松应对。本文将带你一步步构建一个深度学习模型,并…

Chrome 浏览器 支持多账号登录和管理的浏览器容器解决方案

根据搜索结果&#xff0c;目前没有直接提到名为“chrometable”的浏览器容器或插件。不过&#xff0c;从功能描述来看&#xff0c;您可能需要的是一个能够支持多账号登录和管理的浏览器容器解决方案。以下是一些可能的实现方式&#xff1a; 1. 使用 Docker 容器化部署 Chrome …

UdpServer

Udp服务端&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; using System.IO; using …

Kafka 可靠性探究—副本刨析

Kafka 的多副本机制提升了数据容灾能力。 副本通常分为数据副本与服务副本。数据副本是指在不同的节点上持久化同一份数据&#xff1b;服务副本指多个节点提供同样的服务&#xff0c;每个节点都有能力接收来自外部的请求并进行相应的处理。 1 副本刨析 1.1 相关概念 AR&…

智能化转型2.0:从“工具应用”到“价值重构”

过去几年&#xff0c;“智能化”从一个模糊的概念逐渐成为企业发展的核心议题。2024年&#xff0c;随着生成式AI、大模型、智能体等技术的爆发式落地&#xff0c;中国企业正式迈入智能化转型的2.0时代。这一阶段的核心特征是从单一场景的“工具应用”转向全链条的“价值重构”&…

Unity Dots学习

ISystem和SystemBase的区别 Archetype和Chunk 相同组件的实体放在一起&#xff0c;也就是我们所说的内存块&#xff08;Chunk&#xff09; Chunk有一个大小 https://blog.csdn.net/weixin_40124181/article/details/103716338 如果批量操作的entity都是同一个chunk下的效率会更…

【1】高并发导出场景下,服务器性能瓶颈优化

高并发导出场景下&#xff0c;服务器性能瓶颈通常出现在 CPU、内存、磁盘 I/O 或网络带宽等方面。为了解决这些问题&#xff0c;可以从以下几个方面进行优化&#xff1a; 1. 优化导出逻辑 减少计算复杂度&#xff1a;检查导出逻辑中是否存在不必要的计算或重复操作&#xff0c;…

使用 Axios 获取用户数据并渲染——个人信息设置

目录 1. HTML 部分&#xff08;前端页面结构&#xff09; HTML 结构解析&#xff1a; 2. JavaScript 部分&#xff08;信息渲染逻辑&#xff09; JavaScript 解析&#xff1a; 3. 完整流程 4. 总结 5. 适用场景 本文将介绍如何通过 Axios 从服务器获取用户信息&#xff0…

能否通过蓝牙建立TCP/IP连接来传输数据

前言&#xff1a; 最近在做一个项目时&#xff0c;产生了一个疑问&#xff1a;能否通过蓝牙建立TCP/IP连接来传输数据 查阅了一些文章&#xff0c;可以得出结论&#xff1a;不行 下面是我截取的两篇个人认可的文章的回答&#xff1a; 文章一&#xff1a; 蓝牙是一种短距离无…

C06S02-Docker网络和资源限制

一、Docker网络模式 1. 桥接模式 Docker的默认网络模式&#xff0c;工作在第二层&#xff0c;也就是数据链路层。 启动Docker的时候会在宿主机上创建一个虚拟的网桥&#xff0c;工作方式类似于交换机。创建容器的时候&#xff0c;会分配一个和网桥相同网段的IP地址给容器使用…

生产环境的 MySQL事务隔离级别

MySQL 数据库的默认隔离级别是 RR( 可重复读 )&#xff0c;但是很多大公司把隔离级别改成了 RC(读已提交)&#xff0c;主要原因是为了提高并发和降低死锁概率 为了解决幻读的问题 RR 相比 RC 多了间隙锁( gap lock )和临键锁( next-keylock )。而 RC 中修改数据仅用行锁&#…

Oracle(windows安装遇到的ORA-12545、ORA-12154、ORA-12541、ORA-12514等问题)

其实出现该问题就是监听或者服务没有配好。 G:\xiaowangzhenshuai\software\Oracle\product\11.2.0\dbhome_1\NETWORK\ADMINlistener.ora SID_LIST_LISTENER (SID_LIST (SID_DESC (SID_NAME CLRExtProc)(ORACLE_HOME G:\xiaowangzhenshuai\software\Oracle\product\11.2.0\d…

Mac上搭建k8s环境——Minikube

1、在mac上安装Minikube可执行程序 brew cask install minikub 安装后使用minikube version命令查看版本 2、安装docker环境 brew install --cask --appdir/Applications docker #安装docker open -a Docker #启动docker 3、安装kubectl curl -LO https://storage.g…

OpenAI 实战进阶教程 - 第二节:生成与解析结构化数据:从文本到表格

目标 学习如何使用 OpenAI API 生成结构化数据&#xff08;如 JSON、CSV 格式&#xff09;。掌握解析数据并导出表格文件的技巧&#xff0c;以便适用于不同实际场景。 场景背景 假设你是一名开发人员&#xff0c;需要快速生成一批产品信息列表&#xff08;如名称、价格、描述…