红黑树(更高级的二叉查找树)

2023-05-16

目录

介绍及性质

红黑树的基本定义

黑高度

时间复杂度

接近于“平衡”操作

红黑树的旋转

红黑树中插入新结点

红黑树中删除结点

红黑树与AVL树的区别


  • 介绍及性质

  • 红黑树(R-B TREE,全称:Red-Black Tree),本身是一棵二叉查找树,在其基础上附加了两个要求:
    • 1. 树中的每个结点增加了一个用于存储颜色的标志域;
    • 2. 树中没有一条路径比其他任何路径长出两倍,整棵树要接近于“平衡”的状态
  • 这里所指的路径,指的是从任何一个结点开始,一直到其子孙的叶子结点的长度;
  • 接近于平衡:红黑树并不是平衡二叉树,只是由于对各路径的长度之差有限制,所以近似于平衡的状态
  • 红黑树对于结点的颜色设置不是任意的,需满足以下性质的二叉查找树才是红黑树:
    • 树中的每个结点颜色不是红的,就是黑的;
    • 根结点的颜色是黑的;
    • 所有为 nil 的叶子结点的颜色是黑的;(注意:叶子结点说的只是为空(nil 或 NULL)的叶子结点!)
    • 如果此结点是红的,那么它的两个孩子结点全部都是黑的;
    • 对于每个结点,从该结点到到该结点的所有子孙结点的所有路径上包含有相同数目的黑结点;

  • 哪些性质反映了红黑树结构的平衡?
  • 性质5 反映了红黑树结构的平衡
  • 它确保从任意一个节点出发到其叶子节点的所有路径中,最长路径长度也不会超过最短路径长度的两倍;因而,红黑树是相对接近平衡的二叉树
  • 而且性质5 明显指出每个节点的左右子树中黑节点的层数是相等的,因此红黑树的黑节点是完美平衡的
  • 红黑树的基本定义

  • 黑高度

  • 注意:图中每个结点附带一个整形数值,表示的是此结点的黑高度(从该结点到其子孙结点中包含的黑结点数,用 bh(x) 表示(x 表示此结点))
  • nil 的黑高度为 0,颜色为黑色(在编程时为节省空间,所有的 nil 共用一个存储空间)
  • 在计算黑高度时,也看做是一个黑结点
  • 红黑树中每个结点都有各自的黑高度,整棵树也有自己的黑高度,即为根结点的黑高度,例如图中的红黑树的黑高度为 3
  • 时间复杂度

  • 对于一棵具有 n 个结点的红黑树,树的高度至多为:2lg(n+1)
  • 由此可推出红黑树进行查找操作时的时间复杂度为 O(lgn),因为对于高度为 h 的二叉查找树的运行时间为 O(h),而包含有 n 个结点的红黑树本身就是最高为 lgn(简化之后)的查找树(h=lgn),所以红黑树的时间复杂度为 O(lgn)
  • 接近于“平衡”操作

  • 红黑树本身作为一棵二叉查找树,所以其任务就是用于动态表中数据的插入和删除的操作
  • 在进行该操作时,避免不了会破坏红黑树的结构,此时就需要进行适当的调整,使其重新成为一棵红黑树,可以从两个方面着手对树进行调整:
    • 调整树中某些结点的指针结构;
    • 调整树中某些结点的颜色;
  • 红黑树的旋转

  • 当使用红黑树进行插入或者删除结点的操作时,可能会破坏红黑树的 5 条性质,从而变成了一棵普通树
  • 此时就可以通过对树中的某些子树进行旋转,从而使整棵树重新变为一棵红黑树
  • 旋转操作分为左旋和右旋

  • 左旋:如图所示,左旋时 y 结点变为该部分子树的根结点,同时 x 结点(连同其左子树 a)移动至 y 结点的左孩子
  • 若 y 结点有左孩子 b,由于 x 结点需占用其位置,所以调整至 x 结点的右孩子处

  • 右旋:如图所示,同左旋是同样的道理,x 结点变为根结点,同时 y 结点连同其右子树 c 作为 x 结点的右子树,原 x 结点的右子树 b 变为 y 结点的左子树

  • 红黑树中插入新结点

  • 为什么新插入的节点颜色是红的?
  • 将新插入的节点着色为红色,不会违背特性5
  • 少违背一条特性,就意味着我们需要处理的情况越少
  • 当创建一个红黑树或者向已有红黑树中插入新的数据时,只需要按部就班地执行以下 3 步:
    • 由于红黑树本身是一棵二叉查找树,所以在插入新的结点时,完全按照二叉查找树插入结点的方法,找到新结点插入的位置;
    • 将新插入的结点结点初始化,颜色设置为红色后插入到指定位置;(将新结点初始化为红色插入后,不会破坏红黑树第 5 条的性质)
    • 由于插入新的结点,可能会破坏红黑树第 4 条的性质(若其父结点颜色为红色,就破坏了红黑树的性质),此时需要调整二叉查找树,想办法通过旋转以及修改树中结点的颜色,使其重新成为红黑树!
  • 插入结点的第 1 步和第 2 步都非常简单,关键在于最后一步对树的调整!
  • 在红黑树中插入结点时,根据插入位置的不同可分为以下 3 种情况:
  • 1. 插入位置为整棵树的树根;处理办法:只需要将插入结点的颜色改为黑色即可
  • 2. 插入位置的双亲结点的颜色为黑色;处理方法:此种情况不需要做任何工作,新插入的颜色为红色的结点不会破坏红黑树的性质
  • 3. 插入位置的双亲结点的颜色为红色;处理方法:由于插入结点颜色为红色,其双亲结点也为红色,破坏了红黑树第 4 条性质,此时需要结合其祖父结点和祖父结点的另一个孩子结点(父结点的兄弟结点,此处称为“叔叔结点”)的状态,分为 3 种情况讨论:
    • 当前结点的父节点是红色,且“叔叔结点”也是红色:破坏了红黑树的第 4 条性质,解决方案为:将父结点颜色改为黑色;将叔叔结点颜色改为黑色;将祖父结点颜色改为红色;下一步将祖父结点认做当前结点,继续判断,处理结果如下图所示

      • 分析:此种情况下,由于父结点和当前结点颜色都是红色,所以为了不产生冲突,将父结点的颜色改为黑色
      • 但是虽避免了破坏第 4 条,但是却导致该条路径上的黑高度增加了 1 ,破坏了第 5 条性质
      • 但是在将祖父结点颜色改为红色、叔叔结点颜色改为黑色后,该部分子树没有破坏第 5 条性质
      • 但是由于将祖父结点的颜色改变,还需判断是否破坏了上层树的结构,所以需要将祖父结点看做当前结点,继续判断
    • 当前结点的父结点颜色为红色,叔叔结点颜色为黑色,且当前结点是父结点的右孩子;解决方案:将父结点作为当前结点做左旋操作

      • 提示:在进行以父结点为当前结点的左旋操作后,此种情况就转变成了第 3 种情况,处理过程跟第 3 种情况同步进行
    • 当前结点的父结点颜色为红色,叔叔结点颜色为黑色,且当前结点是父结点的左孩子;解决方案:将父结点颜色改为黑色,祖父结点颜色改为红色,从祖父结点处进行右旋处理;如下图所示:

      • 分析:在此种情况下,由于当前结点 F 和父结点 S 颜色都为红色,违背了红黑树的性质 4,此时可以将 S 颜色改为黑色,有违反了性质 5,因为所有通过 S 的路径其黑高度都增加了 1 ,所以需要将其祖父结点颜色设为红色后紧接一个右旋,这样这部分子树有成为了红黑树(上图中的有图虽看似不是红黑树,但是只是整棵树的一部分,以 S 为根结点的子树一定是一棵红黑树)
  • 内部接口 -- insert(node)的作用是将"node"节点插入到红黑树中
  • 外部接口 -- insert(key)的作用是将"key"添加到红黑树中

  •  

  • 红黑树中删除结点

  • 在红黑树中删除结点,思路更简单,只需要完成 2 步操作:
    • 1. 将红黑树按照二叉查找树删除结点的方法删除指定结点;
    • 2. 重新调整删除结点后的树,使之重新成为红黑树;(还是通过旋转和重新着色的方式进行调整)
  • 在二叉查找树删除结点时,分为 3 种情况:
    • 若该删除结点本身是叶子结点,则可以直接删除;
    • 若只有一个孩子结点(左孩子或者右孩子),则直接让其孩子结点顶替该删除结点;
    • 若有两个孩子结点,则找到该结点的右子树中值最小的叶子结点来顶替该结点,然后删除这个值最小的叶子结点
  • 以上三种情况最终都需要删除某个结点,此时需要判断删除该结点是否会破坏红黑树的性质,判断的依据是:
    • 1. 如果删除结点的颜色为红色,则不会破坏;
    • 2. 如果删除结点的颜色为黑色,则肯定会破坏红黑树的第 5 条性质,此时就需要对树进行调整,调整方案分 4 种情况讨论:
      • 删除结点的兄弟结点颜色是红色,调整措施为:将兄弟结点颜色改为黑色,父亲结点改为红色,以父亲结点来进行左旋操作,同时更新删除结点的兄弟结点(左旋后兄弟结点发生了变化),如下图所示:

      • 删除结点的兄弟结点及其孩子全部都是黑色的,调整措施为:将删除结点的兄弟结点设为红色,同时设置删除结点的父结点标记为新的结点,继续判断;
      • 删除结点的兄弟结点是黑色,其左孩子是红色,右孩子是黑色。调整措施为:将兄弟结点设为红色,兄弟结点的左孩子结点设为黑色,以兄弟结点为准进行右旋操作,最终更新删除结点的兄弟结点;
      • 删除结点的兄弟结点是黑色,其右孩子是红色(左孩子不管是什么颜色),调整措施为:将删除结点的父结点的颜色赋值给其兄弟结点,然后再设置父结点颜色为黑色,兄弟结点的右孩子结点为黑色,根据其父结点做左旋操作,最后设置替换删除结点的结点为根结点;
  • 红黑树,虽隶属于二叉查找树,但是二叉查找树的时间复杂度会受到其树深度的影响,而红黑树可以保证在最坏情况下的时间复杂度仍为 O(lgn)
  • 当数据量多到一定程度时,使用红黑树比二叉查找树的效率要高
  • 内部接口 -- remove(node)的作用是将"node"节点插入到红黑树中
  • 外部接口 -- remove(key)删除红黑树中键值为key的节点

  •  

  •  

  • 红黑树与AVL树的区别

  • 1---为什么红黑树比 AVL 树更受欢迎?
  • 红黑树的平衡条件相对宽松,因此在红黑树中插入与删除结点所需的旋转操作相对更少,结点增删操作相比 AVL 树的效率更高
  • 2---和红黑树相比,AVL树是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过1);不管是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而旋转是非常耗时的,由此可以知道AVL树适合用于插入与删除次数比较少,但查找多的情况
  • 3---AVL树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知
  • 4---由于维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,更多的地方是用追求局部而不是非常严格整体平衡的红黑树;当然,如果应用场景中对插入、删除不频繁,只是对查找要求较高,那么AVL还是较优于红黑树
  • 5---相较红黑树AVL树查找性能更快
  • 6---红黑树放弃了追求完全平衡,而是追求大致平衡,在与AVL树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,维持平衡的时间消耗较少,实现起来也更为简单
  • 7---相对于要求严格平衡的AVL树来说,它的旋转次数少,对于插入、删除操作较多的情况下,选择红黑树
  • 8---红黑树的查询性能略微逊色于AVL树,因为其比AVL树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的AVL树最多多一次比较,但是,红黑树在插入和删除上优于AVL树,AVL树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于AVL树为了维持平衡的开销要小得多
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

红黑树(更高级的二叉查找树) 的相关文章

  • jupyter notebook无法打开(或无法用终端打开)

    报错如下 xff1a 解决方法 xff1a 添加这三个环境变量 注 xff1a 这三个路径虽然短 xff0c 但是一定要复制粘贴进去 xff0c 手写很容易报错 xff0c 即使你路径手写是对的 其他问题解决方法 xff1a xff08 1
  • Spring Aop通知注解的执行顺序

    spring4和spring5有所不同 spring4没异常有异常执行顺序从上往下 64 Around通知前 64 Aroud通知前 64 Before通知 64 Before通知业务代码 64 After通知 64 Around通知后 6
  • vcruntime140_1.dll无法继续执行代码如何修复?

    vcruntime140 1 dll是电脑系统动态链接中非常重要的文件 xff0c 主要用于处理各种程序 每台计算机上都有相当多的DLL文件 xff0c 不同的程序会使用不同的DLL文件 电脑系统如果丢失dll文件 xff0c 会导致很多软
  • Linux基础指令的基本操作(一)

    文章目录 Linux用户管理 xff1a 1 adduser添加用户2 passwd修改用户密码3 userdel删除用户 其他指令alias指令 取别名 whoami指令man指令 重要 bc指令unamefreedf h Linux 访
  • Linux 权限(二)权限掩码 粘滞位 详细

    文章目录 Linux权限的概念Linux权限管理01 文件访问者的分类 xff08 人 xff09 02 文件类型和访问权限 xff08 事物属性 xff09 拥有者 xff0c 所属组 xff0c other vs root 和普通用户a
  • Linux——基础IO

    文章目录 先来段代码回顾C文件接口写文件读文件输出信息到显示器 xff0c 你有哪些方法 默认打开的三个流 stdin amp stdout amp stderr系统接口openclosewriteread文件描述符fd文件描述符的分配规则
  • boost字符串库简单使用

    boost字符串库简单使用 说明用法大小写转换字符串分割去掉字符串两边空格替换字符串 replace first replace first copy 说明 写c 43 43 程序的时候 xff0c 虽然std string有数百余函数 x
  • 线程安全下单例模式

    文章目录 什么是单例模式单例模式的特点定义对象的本质什么时候创建对象饿汉实现方式和懒汉实现方式饿汉方式实现单例模式懒汉方式实现单例模式懒汉方式实现单例模式 线程安全版本 什么是单例模式 单例模式是一种 经典的 常用的 常考的 设计模式 单例
  • Linux 线程池

    文章目录 线程池的定义使用线程池的原因基于POSIX实现的线程池基于block队列的线程池实现基于ring队列的线程池实现 设计单例模式线程池 线程池的定义 线程池就一堆已经创建好的任务线程 xff0c 初始它们都处于空闲等待状态 xff0
  • 魔都,3年,程序员到CTO

    过一个平凡无趣的人生实在太容易了 xff0c 你可以不读书 xff0c 不冒险 xff0c 不运动 xff0c 不写作 xff0c 不外出 xff0c 不折腾 但是 xff0c 人生最后悔的事情就是 xff1a 我本可以 陈素封 我可以 在
  • TCP协议

    文章目录 1 保证可靠性机制1 1 确认应答机制1 1 1确认应答机制概念1 1 2常规确认应答的工作方式1 1 3报文按序到达1 1 4 如何确认历史数据被收到1 1 5 16位序号和16确认序号 xff08 字段讲解 xff09 tcp
  • 1 对数器,二分查找,

    文章目录 对数器二分查找 1 有序序列二分查找 2 在一个有序数组中 xff0c 找 lt 61 某个数最右侧的位置 3 在一个有序数组中 xff0c 找 gt 61 某个数最左侧的位置 4 无序序列二分查找 xff0c 求局部最小值 对数
  • 2 异或位运算大厂必刷题

    文章目录 如何不用额外变量交换两个数一个数组中有一种数出现了奇数次 xff0c 其他数都出现了偶数次 xff0c 怎么找到并打印这种数怎么把一个int类型的数 xff0c 提取出最右侧的1来怎么把一个int类型的数 获取位数为1的数量一个数
  • 链表,栈,队列,递归行为,哈希表,有序表

    文章目录 链表1 单链表 双链表的反转2 删除链表中指定的值 队列1 数组循环队列的实现2 双向链表实现双端队列 栈1 用数组实现栈 栈和队列的面试题1 实现最小栈2 两个栈实现一个队列3 两个队列实现一个栈4 用栈实现图的广度优先遍历5
  • 搭建Zabbix6.0版本

    Zabbix简介 Zabbix是一个企业级的开源分布式监控解决方案 xff0c 由C语言编写而成的底层架构 xff08 server端和agent端 xff09 xff0c 由一个国外的团队持续维护更新 xff0c 软件可以自由下载使用 x
  • Linux--网络服务器配置步骤详情【1】

    目录 一 配置ip地址 二 配置yum服务器 三 配置安装nfs服务器 1 第一台机 xff1a 2 第二台机 xff1a 四 安装配置samba服务器 五 安装配置DHCP 一 配置ip地址 root 64 wenjian vi etc
  • vscode提取拓展时出错。XHR failed

    vscode提取拓展时出错 XHR failed huas weew12的博客 CSDN博客 提取扩展时出错 转载 这这人家的步骤操作 果然就好了
  • python天气语音播报

    今天的小项目是一个天气播报 xff0c 项目效果是点击运行就读出今天的天气 那么我们可以分两步走 xff0c 第一个 xff1a 先爬取到今天的天天气内容 xff0c 第二步 xff1a 电脑读出今天的天气内容 想要电脑读出内容 xff0c
  • Linux配置SSH远程登录管理

    目录 一 SSH协议 1 SSH简介 2 SSH的优点 3 SSH远程控制软件及服务 二 SSH远程管理配置 1 配置OpenSSH服务端 2 使用SSH客户端软件 xff08 1 xff09 SSH远程登录 xff08 2 xff09 s
  • Linux系统防火墙firewalld

    目录 一 firewalld概述 二 firewalld和iptables的关系 三 firewalld区域的概念 四 firewalld数据处理流程 五 firewalld检查数据包源地址的规则 六 firewalld防火墙的配置种类 1

随机推荐