要掌握红黑树?仍是得理解红黑树的删除跟插入过程,理解红黑树的操做来源!

引言

预防针:红黑树原本就是基本算法中的难点,因此看此文时建议先有点预备心理或知识铺垫,没接触过RBT而直接看此文的话,绝对懵逼。面试

为了数据的查询跟增删方便,系统引入了二叉查找树,它具备左节点 < 跟节点 <右节点 的属性,算法

二叉查找树,可是这种设定跟数据的插入顺序有很大关系,好比你插入的是1234,二叉查找树会退化为链表。markdown

链表,为了不链表结构的出现,研究者们又提出了平衡二叉树跟红黑树。平衡二叉树要求任意一个节点的左深度跟右深度差值绝对值不能大于1,若是插入后超过了1会经过左右各类旋转来更改链接的变化,最终实现左右深度差不大于1的这个要求。数据结构

平衡二叉树的深度要求太过完美,当涉及大量增删时,可能会太多时间用在调节平衡上,为了平衡投入跟产出比,又设计了红黑树。oop

红黑树算是一个比较复杂的数据结构了,除非你面字节,可能让你手写红黑树。通常状况下你只要说出红黑树构造的五大背后逻辑,展示你对底层数据结构的深度跟广度便可。性能

本文不会着重说红黑树的增删过程,由于你百度看下权威教程或源码,而后跟着追踪就知道大体流程了,本文会说下红黑树为什么如此设计,它跟2-3树有啥联系。学习

定义

为了保证查找树的平衡性,须要一些灵活性,所以咱们容许树中的一个结点保存多个键。spa

2结点:含有一个键和两条连接,左连接指向的2-3树中的键都小于该结点,右连接指向的2-3树中的键都大于该结点。设计

3结点:含有两个键和三条连接,左连接指向的2-3树中的键都小于该结点,中连接指向的2-3树中的键都位于该结点的两个键之间,右连接指向的2-3树中的键都大于该结点。3d

4节点:含有三个键和四条连接,大体的思路跟3节点相似。需注意在2-3树中,4节点是短暂存在的,会被转化为2节点或3节点。

查找

要判断一个键是否在树中,咱们先将它和根结点中的键比较。若是它和其中的任何一个相等,查找命中。不然咱们就根据比较的结果找到指向相应区间的连接,并在其指向的子树中递归地继续查找。若是这是个空连接,查找未命中,能够发现跟简单的二叉树查找相似。

插入

要在2-3树中插入一个新结点,咱们能够和二叉查找树同样先进行一次未命中的查找,而后把新结点挂在树的底部。但这样的话树没法保持完美平衡性。使用2-3树的主要缘由就在于它可以在插入以后继续保持平衡。

若是未命中的查找结束于一个2结点:只要把这个2结点替换为一个3结点,将要插入的键保存在其中便可。 只有一个3结点的树,向其插入一个新数据:此时咱们能够建立个临时4节点,而后将其转化为由3个2节点组成的2-3树

只有3节点树插入数据

向一个父结点为2结点的3结点中插入新键:此时先将组成个临时4节点,而后将中间数提到上面跟父节点融合为一个3节点,这样树的高度没变。

向一个父结点为3结点的3结点中插入新键4:跟上面套路相似,不断将中位数的数据往上提,直到遇到个2节点,或者到达了根节点而后进行拆分。

![(p3-juejin.byteimg.com/tos-cn-i-k3…)

插入总结:

先找插入结点,若结点是2结点,则直接插入。如结点3结点,则插入使其临时容纳这个元素,而后分裂此结点,把中间元素移到其父结点中。对父结点亦如此处理。(中键一直往上移,直到找到空位,在此过程当中没有空位就先搞个临时的,再分裂。) 2-3树插入算法的根本在于这些变换都是局部的:除了相关的结点和连接以外没必要修改或者检查树的其余部分。每次变换中,变动的连接数量不会超过一个很小的常数。全部局部变换都不会影响整棵树的有序性和平衡性。

删除

2-3树的删除分为两种状况。

若是待删除元素在3节点,那么能够直接将这个元素删除,删除这个元素不会引发高度的变化。

删除3节点中数据

当待删除元素在2节点时,因为删除这个元素会致使2节点失去惟一的元素,引起树中某条路径的高度发生变化,为维持平衡,此时有两种方法。

先删除再对2-3树进行平衡调整。

想办法让这个被删除的元素不可能出如今2节点中。若是发现删除元素树2节点则会从兄弟节点或父节点借个元素,当前2节点变为3节点或临时4节点,而后再删除目标数据。

2节点状况下删除目标数据2

构造

和标准的二叉查找树由上向下生长不一样,2-3树的生长是由下向上的。

插入

优势、缺点

优势:

2-3树在最坏状况下仍有较好的性能。每一个操做中处理每一个结点的时间都不会超过一个很小的常数,且这两个操做都只会访问一条路径上的结点,因此任何查找或者插入的成本都确定不会超过对数级别。

完美平衡的2-3树要平展的多。例如含有10亿个结点的一颗2-3树的高度仅在19到30之间。咱们最多只须要访问30个结点就能在10亿个键中进行任意查找和插入操做。

缺点:

咱们须要维护两种不一样类型的结点,查找和插入操做的实现须要大量的代码,并且它们所产生的额外开销可能会使算法比标准的二叉查找树更慢。

平衡一棵树的初衷是为了消除最坏状况,但咱们但愿这种保障所需的代码可以越少越好,越简单越好,显然2-3树也不太适合。

既然已经懂了2-3树的实现,接下来咱们对2-3树稍微变型下就是红黑树了,你能够认为红黑树的本质实际上是对概念模型2-3-4树的一种实现。

红黑树

树跟红黑树关联

因为直接进行不一样节点间的转化会形成较大的开销,因此选择以二叉树为基础,在二叉树的属性中加入一个颜色属性来表示2-3树中不一样的节点。

2-3树中的2节点对应着红黑树中的黑色节点。

2-3树中的非2节点是以红节点 + 黑节点的方式存在,红节点的意义是与黑色父节点结合,表达着2-3树中的3,4节点。有的书上把红色说成了红色连接,也是一直理解方法。

先看2-3树到红黑树的节点转换。2节点直接转化为黑色节点。3节点这里能够有两种表现形式,左倾红节点或者右倾红节点。而4节点被强制要求转化为一个黑父带着左右两个红色儿子。

23树到红黑树转变 因为3节点转化到时候能够左倾也能够右倾,若是查看算法书籍,你会发现为了简单化,算法书籍中统一规定只用左倾红黑树。

红黑树跟2-3树转化到时候,能够认为将红色节点顺时针上升45度,来跟它到父节点保持平衡,再将红色到跟父节点看做一个总体。

红黑树转2-3树,能够发现黑色节点才会真正在2-3树中增长高度,因此红黑树的完美平衡其实等价2-3树的根节点到叶子节点到距离相等。因此说红黑树是2-3树或2-3-4树概念模型的一种实现。

在算法导论中红黑树树基于2-3-4树实现的。

在算法4中红黑树树基于2-3树实现的,而且要求3节点在红黑树中必须以左倾红色节点来表示。

2-3树确定比2-3-4树简单,因此接下来主要基于2-3树说。

红黑树基础定义跟旋转

五大法则

节点有黑色跟红色两种:至于为什么有红色节点,在2-3树中已经说过了。

根节点必须是黑色:2-3树中若是根节点树2节点那原本就是黑色,若是是3节点就用大的当黑根节点,小的当左倾红节点。

叶子节点为不存数据且都是黑色:主要是为了在插入跟删除时候方便操做。

任意节点到叶子节点通过的黑色节点数目相同:红黑树中的红节点是和黑色父节点绑定的,在2-3树中原本就是同一层的,只有黑色节点才会在2-3树中真正贡献高度,因为2-3树的任一节点到空连接距离相同,所以反应在红黑树中就是黑色完美平衡。

不会有连续的红色节点:2-3树中原本就规定没有4节点,2-3-4树中虽然有4节点,可是要求在红黑树中体现为一黑色节点带两红色儿子,分布左右,因此也不会有连续红节点。

左旋跟右旋

红黑树要求新插入数据颜色是红色,黑色是改变后的结果。红黑树的核心是左旋跟右旋。

左旋跟右旋

左倾红黑树插入

左倾红黑树的插入一共有三种可能的状况。

待插入元素比黑父大,插在了黑父的右边,而黑父左边是红色儿子。这种状况会致使在红黑树中出现右倾红节点。或者黑父左边为空也会出现右倾。

插入20

待插入元素比红父小,且红父自身就是左倾,待插入数据比红父左节点还小,造成了连续的红节点。

对红父的父亲节点进行一次右旋转。

将数据变化为状况1的状态处理。

image.png

插入14

待插入元素比红父大,且红父自身就是左倾。待插入数据比红父左节点大,造成了右倾。经过左旋变成状况2处理。

插入17 总体来讲左倾红黑树的插入就是这3种状况来回切换,最终达到平衡。

左倾红黑树删除

删除思路是不删除目标数据,而是找到目标数据的前驱节点或后继节点,而后把数据拷贝一份到目标数据进行覆盖。而后转而去删除前驱或后继。删除后再去修补平衡。

从宏观上来看从根节点开始查找,全程利用2-3树思惟逐层对红黑树调整,每次保证当前节点树2-3树中非2节点,若是是非2节点则看下一层,若是是2节点则根据兄弟节点调整。

兄弟节点是2节点,从父节点借个数据跟当前节点及兄弟节点造成临时4节点。

兄弟节点是非2节点,兄弟节点上升一个数据,父节点降低一个数据。

删除目标1 删除后就涉及到数据平衡修复了,仍是根据2-3树来修复平衡,路上可能会碰到红色右倾节点,遇到就进行一次左旋便可。

2-3树修补工做

工业级红黑树增长

这里其实主要参考极客时间小争哥的文章,说下实际工程中红黑树的增删操做,增长主要有3种状况:

状况1:关注节点是 a,它的叔叔节点 d 是红色:

将关注节点 a 的父节点 b、叔叔节点 d 的颜色都设置成黑色。

将关注节点 a 的祖父节点 c 的颜色设置成红色。

关注节点变成 a 的祖父节点 c,实现关注节点的迁移。

跳到状况2或状况3。

image.png

状况2:关注节点是 a,它的叔叔节点 d 是黑色,关注节点 a 是其父节点 b 的右子节点:

关注节点变成节点 a 的父节点 b。

围绕新的关注节点 b 左旋。

跳到状况3。

image.png

状况3:若是关注节点是 a,它的叔叔节点 d 是黑色,关注节点 a 是其父节点 b 的左子节点,咱们就依次执行下面的操做:

围绕关注节点 a 的祖父节点 c 右旋。

将关注节点 a 的父节点 b、兄弟节点 c 的颜色互换,调整结束。

工业级红黑树删除

相比插入,删除就难多了!核心思想是找准关注点,根据关注点跟周围节点排布特征按照必定规则调整。主要俩步骤:

针对删除节点调整后仍要知足节点到叶子节点路径包含相同黑色节点。

针对关注节点二次调整,防止出现2个红色节点。

算法导论中说若是删除黑节点X带来黑色平衡破坏,让X的子节点变为黑-黑或红-黑。意思是既然删除了某个黑色节点,那么必然会破坏以这个黑色节点为路径上的黑色平衡,表现为路径中缺乏一个黑,因此要想办法补充一个黑色节点(下面会用黑色圆圈表示)。同时若是一个节点既能够红又能够黑,就用红黑两个组成部分表示。

删除第一步

状况1:要删除的节点是 a,它只有一个子节点 b:

删除节点 a,而且把节点 b 替换到节点 a 的位置,这一部分操做跟普通的二叉查找树的删除操做同样。

节点 a 只能是黑色,节点 b 也只能是红色,其余状况均不符合红黑树的定义。此时把节点 b 改成黑色。调整结束,不须要进行二次调整。

状况2:要删除的节点 a 有两个非空子节点,而且它的后继节点就是节点 a 的右子节点 c:

若是节点 a 的后继节点就是右子节点 c,那 c 确定没有左子树。将c的颜色变为a的颜色,而且用c来覆盖a。

若是节点 c 是黑色,为了避免违反红黑树的路径相同原则,给节点 c 的右子节点 d 多加一个黑色圆圈,这个时候节点 d 就成了红 - 黑或者黑 - 黑。

此时关注节点变成了节点 d,第二步的调整操做就会针对关注节点来作。

状况3:要删除的是节点 a,它有两个非空子节点,而且节点 a 的后继节点不是a的右子节点:

找到后继节点 d,并将它删除,删除后继节点 d 的过程参照 CASE 1。

用d来替换a,而且d的颜色设置的跟a颜色同样。

若是节点 d 是黑色,为了避免违反红黑树路径相同原则,给节点 d 的右子节点 c 多加一个黑色,这个时候节点 c 就成了红 - 黑或者黑 - 黑。

此时关注节点变成了节点 c,第二步的调整操做就会针对关注节点来作。

删除第二步

通过初步调整以后,关注节点变成了红 - 黑或者黑 - 黑    节点。针对这个关注节点,再分四种状况来进行二次调整。二次调整是为了让红黑树中不存在相邻的红色节点。

i

状况1:关注节点是 a,它的兄弟节点 c 是红色的,咱们就依次进行下面的操做:

围绕关注节点 a 的父节点 b 左旋。

关注节点 a 的父节点 b 和祖父节点 c 交换颜色。

关注节点不变,继续从四种状况中选择适合的规则来调整。

状况2:关注节点是 a,它的兄弟节点 c 是黑色,而且节点 c 的左右子节点 d、e 都是黑色:

将关注节点 a 的兄弟节点 c 的颜色变成红色,由于接下来黑圆圈会上移,那么c比a多个深色。

从关注节点 a 中去掉一个黑色,此时节点 a 就是单纯的红色或者黑色。

给关注节点 a 的父节点 b 添加一个黑色,这个时候节点 b 就变成红 - 黑或者黑 - 黑

关注节点从 a 变成其父节点 b,继续从四种状况中选择符合的规则来调整。

状况3:关注节点是 a,它的兄弟节点 c 是黑色,c 的左子节点 d 是红色,c 的右子节点 e 是黑色:

围绕关注节点 a 的兄弟节点 c 右旋。

节点 c 和节点 d 交换颜色。

关注节点不变,跳转到状况4,继续调整。

状况4:关注节点 a 的兄弟节点 c 是黑色的,而且 c 的右子节点是红色的,咱们就依次进行下面的操做:

围绕关注节点 a 的父节点 b 左旋。

将b的颜色复制给c,由于c替代了b的位置。

将关注节点 a 的父节点 b 的颜色设置为黑色。

从关注节点 a 中去掉一个黑色,节点 a 就变成了单纯的红色或黑色。

将关注节点 a 的叔叔节点 e 设置为黑色,调整结束。

此时a跟d深度是同样的,由于没法判别ad是否为红,直接将b设置为黑的了,此时e提升了一度为保持平衡也设置为黑色的了。

删除理解

多画图,不画图单看代码一下子就眩晕了。

插入跟删除算法都是用到了递推,好比插入状况1,状况1的处理以后,关注节点从自己变成了它的祖父红色节点,这就是往根节点递推。不过状况1处理过一次以后,不必定会进入状况2或者状况3,有可能还在状况1。在状况1的状况下一直往根节点走,由于当前节点永远是红色,因此在最后要把根节点涂黑。同时只要进入到状况二、状况3状况,操做就跟上面说过的相似了。

要记住,除了关注的节点所在的子树,其余的子树自己都是一颗红黑树,它们是知足红黑树的全部特征的。当关注节点往根节点递推时,这个时候关注节点的子树也已经知足了红黑树的定义,咱们就不用再去特别关注子树的特征。只要注意关注节点往上的部分。这样就能把问题简化,思考的时候思路会清晰一些。

再说到删除算法,注意红-黑跟黑-黑存在的缘由,为什么最终都会走到从兄弟节点的地方作文章来实现最终的平衡。

删除状况1的目的只是为了可以进入接下来的三个状况中。

删除状况2的套路又是一个递推思路,关注节点往根节点递推,让其左右子树都知足红黑树的定义。由于往上推,右子树多了一个黑色节点,就把关注节点的兄弟节点变红。

删除状况3是为了进入删除状况4,提早变色的缘由和状况2是同样的,都是为了知足黑色深度相同。一样是概括推理的思路。都要记住一点,各类状况下的其余子树节点都知足红黑树的定义,须要分类讨论的,都在这几种状况中了。

可能你看今天看了红黑树的删除你顿悟了,过了半个月又迷糊了。不要怕!由于怕也没用,再看呗。学习红黑树自己也不是为了面试字节去默写,而是去学习思想,锻炼思惟,复杂问题简单化,固然了顺带也能够装的一手好B。

总结

本文的重点不在于讲解工业化红黑树的删除跟插入所有过程,只是但愿经过2-3树跟左倾红黑树的增删,让你们从本质上理解下红黑树的操做来源。其中工业化删除部分已征得小争哥赞成,若是理解了上面的内容,那么你再去看工业化红黑树的操做就手到擒来了。

相关文章
相关标签/搜索