当前位置: 首页 > 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.xdnf.cn/news/1231471.html

相关文章:

  • 【LeetCode 热题 100】(四)子串
  • 前端-移动Web-day3
  • 云环境K8s集群WebSocket连接失败解决方案
  • 【REACT18.x】使用vite创建的项目无法启动,报错TypeError: crypto.hash is not a function解决方法
  • 基于 LightGBM 的二手车价格预测
  • GaussDB having 的用法
  • 图像加密学习日志————论文学习DAY4
  • 分布式事务----spring操作多个数据库,事务以及事务回滚还有用吗
  • 机械臂的轨迹生成的多种方案
  • Jupyter notebook如何显示行号?
  • MFC 实现托盘图标菜单图标功能
  • NCV8402ASTT1G自保护N沟道功率MOSFET安森美/ONSEMI 过流过温保护汽车级驱动NCV8402ASTT1
  • 从基础功能到自主决策, Agent 开发进阶路怎么走?
  • 【计算机网络】Socket网络编程
  • Android 15 限制APK包手动安装但不限制自升级的实现方案
  • 断路器瞬时跳闸曲线数据获取方式
  • Javaweb————Apache Tomcat服务器介绍及Windows,Linux,MAC三种系统搭建Apache Tomcat
  • 嵌入式第十八课!!数据结构篇入门及单向链表
  • Oracle 11gR2 Clusterware应知应会
  • IDM下载失败排查
  • 704. 二分查找
  • 市政污水厂变频器联网改造方案-profibus转ethernet ip网关(通俗版)
  • CommonJS和ES6 Modules区别
  • python:以支持向量机(SVM)为例,通过调整正则化参数C和核函数类型来控制欠拟合和过拟合
  • Autosar Nm-网管报文PNC停发后无法休眠问题排查
  • 区分「尊重」和「顺从」
  • 简化理解I2C总线
  • Android13文件管理USB音乐无专辑图片显示的是同目录其他图片
  • 【Django】-6- 登录用户身份鉴权
  • Piriority_queue