当前位置: 首页 > news >正文

红黑树(RBTree)

印象里面在数据查找当中,常见的数据结构有 红黑树、哈希表、b/b+树、跳表

一、红黑树

1.1、应用场景

个人印象里面,在以下场景中使用较多:

  • C++ STL中的map,set
  • epoll底层数据结构之一
  • Linux的进程调度(CFS)
  • nginx的定时器管理
  • 各种含有key,value的数据查找场景

1.2、定义

红黑树是一种近似平衡的二叉查找树,它能够确保任何一个节点的左右子树的高度差不会超过2。

1.3、特性

  • 每个节点都是红的或者黑的
  • 根节点是黑的
  • 每个叶子节点是黑的
  • 如果一个节点是红的,则它的两个子节点都是黑的
  • 对每个节点,从该节点到其子孙节点的所有路径上包含相同数量的黑节点------决定红黑树的高度
    在这里插入图片描述

在这里插入图片描述

1.4、从根节点出发,最长路径最大能包含多少节点?

从根节点出发,红黑树的最短路径和最长路径的节点数差异是2倍。这意味着,如果最短路径包含n个节点,那么最长路径将包含最多2n-1个节点。

二、具体实操

2.1、如何定义红黑树?

typedef struct _rbtree_node{int key;void* value;struct _rbtree_node* left;struct _rbtree_node* right;struct _rbtree_node* parent;unsigned char color; // 0: red, 1: black
}rbtree_node;// 叶子节点都是黑色的,可以定义为空节点,为了好判断,不能定义为NULL,否则是非法操作
typedef struct _rbtree{struct _rbtree_node* root;struct _rbtree_node* nil; // 叶子节点,黑色的空节点int size; // 树中节点的数量
}rbtree;

2.2、旋转:

  • 红黑树性质被破坏的时候会发生旋转
    在这里插入图片描述
void leftRotate(rbtree* tree, rbtree_node* x)
{rbtree_node* y = x->right; // 右子节点// 断x与y的父子关系x->right = y->left; // 让左子节点成为右子节点的父节点if(y->left != tree->nil) // 如果左子节点不是叶子节点,则更新父节点的指向{y->left->parent = x;}// 断x与x父节点的父子关系,并建立x父节点与y的父子关系y->parent = x->parent; // 让y的父节点指向x的父节点if(x->parent == tree->nil) // 如果x是根节点,则更新根节点的指向{tree->root = y;}else if(x == x->parent->left) // 如果x是其父节点的左子节点,则更新其父节点的左子节点{x->parent->left = y;}else // 如果x是其父节点的右子节点,则更新其父节点的右子节点{x->parent->right = y;}// 更新x与y的父子关系y->left = x; // 让x成为y的左子节点x->parent = y; // 更新父节点的指向
}// 将左旋反过来,x变y,left变right,right变left
void rightRotate(rbtree* tree, rbtree_node* y)
{rbtree_node* x = y->left; // 左子节点// 断y与x的父子关系y->left = x->right; // 让右子节点成为左子节点的父节点if(x->right != tree->nil) // 如果右子节点不是叶子节点,则更新父节点的指向{x->right->parent = y;}// 断y与y父节点的父子关系,并建立y父节点与x的父子关系x->parent = y->parent; // 让x的父节点指向y的父节点if(y->parent == tree->nil) // 如果y是根节点,则更新根节点的指向{tree->root = x;}else if(y == y->parent->right) // 如果y是其父节点的右子节点,则更新其父节点的右子节点{y->parent->right = x;}else // 如果y是其父节点的左子节点,则更新其父节点的左子节点{y->parent->left = x;}// 更新y与x的父子关系x->right = y; // 让y成为x的右子节点y->parent = x; // 更新父节点的指向
}

2.3、插入:

  • 插入节点默认为红色
void rbtree_insert(rbtree* tree, rbtree_node* node)
{rbtree_node* y = tree->nil;  // 记录x的父节点rbtree_node* x = tree->root; // 根节点// 找到插入的位置while(x != tree->nil) // 如果不是叶子节点,则继续查找{   y = x; // 记录父节点if(node->key < x->key) // 如果插入节点的键小于当前节点的键,则向左子节点查找{x = x->left;}else if(node->key > x->key) // 如果插入节点的键大于等于当前节点的键,则向右子节点查找{x = x->right;}else{      // 如果插入节点的键等于当前节点的键,则替换当前节点x->value = node->value;}}// 如果是空红黑树,则直接插入if(y == tree->nil) // 如果y是叶子节点,则直接插入{tree->root = node; // 更新根节点}else{if(y->key > node->key) // 如果插入节点的键小于父节点的键,则将插入节点设置为左子节点{y->left = node;}else // 如果插入节点的键大于父节点的键,则将插入节点设置为右子节点{y->right = node;}}node->parent = y; // 更新父节点node->left = tree->nil;node->right = tree->nil;node->color = 0 ; // 新插入的节点默认为红色,不改变黑高// 插入之后,进行旋转和变色insertFixup(tree, node);
}

以N-新节点,P-父节点,G-祖父节点,U-叔父节点的命名方式来描述插入之后的调整过程。

情况条件调整
1N是根节点将N的颜色改为黑色,结束调整。
2U是红色,P是G的左子节点将P和U的颜色改为黑色,G的颜色改为红色,然后以G作为新的N继续向上调整
3U是红色,P是G的右子节点将P和U的颜色改为黑色,G的颜色改为红色,然后以G作为新的N继续向上调整
4U是黑色,P是G的左子节点,N是P的左子节点(LL)将P的颜色改为黑色,G的颜色改为红色,然后对G进行右旋。
5U是黑色,P是G的左子节点,N是P的右子节点(LR)对P进行左旋,N的颜色改为黑色,G的颜色改为红色,对G进行右旋。
6U是黑色,P是G的右子节点,N是P的左子节点(RL)对P进行右旋,N的颜色改为黑色,G的颜色改为红色,对G进行左旋。
7U是黑色,P是G的右子节点,N是P的右子节点(RR)将P的颜色改为黑色,G的颜色改为红色,然后对G进行左旋。

默认叶子节点不出现
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

// 插入之后的调整
void insertFixup(rbtree* tree, rbtree_node* node)
{rbtree_node* parent = node->parent;rbtree_node* grandparent = NULL;rbtree_node* uncle = NULL;// 如果节点本身是红色while(parent->color == 0) // 如果父节点是红色,那么祖父节点必是黑色,叔父节点可能是红色或黑色{grandparent = parent->parent; // 获取祖父节点if(parent == grandparent->left) // 如果父节点是其祖父节点的左子节点{uncle = grandparent->right; // 叔父节点// 情况2和3:叔父节点是红色if (uncle->color == 0){parent->color = 1; // 将父节点变为黑色uncle->color = 1; // 将叔父节点变为黑色grandparent->color = 0; // 将祖父节点变为红色node = grandparent; // 继续向上调整}else // 叔父节点是黑色{// 情况4:N是P的左子节点(LL)if (node == parent->left){rightRotate(tree, grandparent); // 对祖父节点进行右旋parent = node->parent; // 更新父节点}// 情况5:N是P的右子节点(LR)else{leftRotate(tree, parent); // 对父节点进行左旋parent = node->parent; // 更新父节点rightRotate(tree, grandparent); // 对祖父节点进行右旋}parent->color = 1; // 将父节点变为黑色grandparent->color = 0; // 将祖父节点变为红色node = parent; // 继续向上调整}}else // 父节点是其祖父节点的右子节点{uncle = grandparent->left; // 获取叔父节点// 情况2和3:叔父节点是红色if (uncle->color == 0){parent->color = 1; // 将父节点变为黑色uncle->color = 1; // 将叔父节点变为黑色grandparent->color = 0; // 将祖父节点变为红色node = grandparent; // 继续向上调整}else // 叔父节点是黑色{// 情况6:N是P的左子节点(RL)if (node == parent->left){rightRotate(tree, parent); // 对父节点进行右旋parent = node->parent; // 更新父节点leftRotate(tree, grandparent); // 对祖父节点进行左旋}// 情况7:N是P的右子节点(RR)else{leftRotate(tree, grandparent); // 对祖父节点进行左旋parent = node->parent; // 更新父节点}parent->color = 1; // 将父节点变为黑色grandparent->color = 0; // 将祖父节点变为红色node = parent; // 继续向上调整}}}tree->root->color = 1; // 将根节点变为黑色
}

在这里插入图片描述

三、问题

3.1、map为什么不用avl树?

AVL树是一种严格平衡的二叉树,要求每个节点的左右子树的高度最多相差1,在大量地进行插入和删除操作时,AVL树需要频繁地进行旋转来维持平衡状态,这会导致较高的时间复杂度。

而RB树(红黑树)是非严格的平衡二叉树,只要从根节点到叶子节点的最长路径不超过最短路径的2倍,那么就不用进行频繁地进行平衡调节,使得插入、删除、查找效率稳定在O(logn)级别。

Code
0vice·GitHub

http://www.dtcms.com/a/311215.html

相关文章:

  • Redis面试精讲 Day 7:GEO地理位置应用详解
  • Mysql在页内是怎么查找数据的?
  • 第14届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2022年11月真题
  • web练习
  • 初始C语言---第四讲(数组)
  • Tlias案例-登录 退出 打包部署
  • 【自动化运维神器Ansible】YAML语法详解:Ansible Playbook的基石
  • 【自动化运维神器Ansible】YAML支持的数据类型详解:构建高效Playbook的基石
  • 人工智能与农业:智慧农业的发展与未来
  • 基于Postman进行http的请求和响应
  • 基于SpringBoot+MyBatis+MySQL+VUE实现的在线课程管理系统(附源码+数据库+毕业论文+开题报告+部署教程+配套软件)
  • Ubuntu系统VScode实现opencv(c++)图像放缩与插值
  • 高效轻量的C++ HTTP服务:cpp-httplib使用指南
  • Android 15 限制APK包手动安装但不限制自升级的实现方案
  • 把“多视图融合、深度传感”组合在一起,今天分享3篇3D传感技术干货
  • [硬件电路-120]:模拟电路 - 信号处理电路 - 在信息系统众多不同的场景,“高速”的含义是不尽相同的。
  • Word怎样转换为PDF
  • Qwen3 Embedding:新一代文本表征与排序模型
  • 2411. 按位或最大的最小子数组长度
  • Django开发中医针灸经络图系统实战
  • 【iOS】3GShare仿写
  • 【Linux网络】netstat 的 -anptu 各个参数各自表示什么意思?
  • 2025 年 VSCode 插件离线下载硬核攻略
  • 打破传统养育框架:梁婉昕的 “非矫正式教育” 探索|创客匠人
  • 八股取士--docker
  • 在 AKS 中运行 Azure DevOps 自托管代理-2
  • 贪心算法应用:3D打印支撑结构问题详解
  • CommonJS和ES6 Modules区别
  • 如何安装和使用 Cursor AI 编辑器
  • 深度解读 | 斯坦福:2025 AI 指数报告