预防针:红黑树本来就是基本算法中的难点,所以看此文时建议先有点预备心理或知识铺垫,没接触过RBT而直接看此文的话,绝对懵逼。
为了数据的查询跟增删方便,系统引入了二叉查找树,它具有左节点 < 根节点 <右节点
的属性,
二叉查找树(大于跟小于界线应为8,不是12)
但是这种设定跟数据的插入顺序有很大关系,比如你插入的是1234,二叉查找树会退化为链表
。
链表
为了避免链表结构的出现,研究者们又提出了平衡二叉树
跟红黑树
。平衡二叉树要求任意一个节点的左深度跟右深度差值绝对值不能大于1,如果插入后超过了1会通过左右各种旋转来更改连接的变化,最终实现左右深度差不大于1的这个要求。
平衡二叉树的深度要求太过完美,当涉及大量增删时,可能会太多时间用在调节平衡上,为了平衡投入跟产出比,又设计了红黑树
。
红黑树
算是一个比较复杂的数据结构了,除非你面字节,可能让你手写红黑树。一般情况下你只要说出红黑树构造的五大背后逻辑,展现你对底层数据结构的深度跟广度即可。
本文不会着重说红黑树的增删过程,因为你百度看下权威教程或源码,然后跟着追踪就知道大致流程了,本文会说下红黑树为何如此设计,它跟2-3树有啥联系。
为了保证查找树的平衡性,需要一些灵活性,因此我们允许树中的一个结点保存多个键。
2结点
:含有一个键和两条链接,左链接指向的2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。3结点
:含有两个键和三条链接,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。4节点
:含有三个键和四条链接,大致的思路跟3节点类似。需注意在2-3树中,4节点是短暂存在的,会被转化为2节点或3节点。
2-3-4节点
要判断一个键是否在树中,我们先将它和根结点中的键比较。如果它和其中的任何一个相等,查找命中。否则我们就根据比较的结果找到指向相应区间的链接,并在其指向的子树中递归地继续查找。如果这是个空链接,查找未命中,可以发现跟简单的二叉树查找类似。
要在2-3树中插入一个新结点,我们可以和二叉查找树一样先进行一次未命中的查找,然后把新结点挂在树的底部。但这样的话树无法保持完美平衡性。使用2-3树的主要原因就在于它能够在插入之后继续保持平衡。
只有3节点树插入数据
插入25
插入4
插入总结:
2-3树的删除分为两种情况。
删除3节点中数据
2节点情况下删除目标数据2
和标准的二叉查找树由上向下生长不同,2-3树的生长是由下向上的
。
插入
优点:
缺点:
既然已经懂了2-3树的实现,接下来我们对2-3树稍微变型下就是红黑树了,你可以认为红黑树的本质其实是对概念模型2-3-4树的一种实现
。
由于直接进行不同节点间的转化会造成较大的开销,所以选择以二叉树为基础,在二叉树的属性中加入一个颜色属性来表示2-3树中不同的节点。
先看2-3树到红黑树的节点转换。2节点直接转化为黑色节点。3节点这里可以有两种表现形式,左倾红节点或者右倾红节点。而4节点被强制要求转化为一个黑父带着左右两个红色儿子。
23树到红黑树转变 由于3节点转化到时候可以左倾也可以右倾,如果查看算法书籍,你会发现为了简单化,算法书籍中统一规定只用左倾红黑树。
红黑树跟2-3树转化到时候,可以认为将红色节点顺时针上升45度,来跟它到父节点保持平衡,再将红色到跟父节点看作一个整体。
红黑树转2-3树 可以发现黑色节点才会真正在2-3树中增加高度,所以红黑树的完美平衡其实等价2-3树的根节点到叶子节点到距离相等。所以说红黑树是2-3树或2-3-4树概念模型的一种实现。
红黑树要求新插入数据颜色是红色,黑色是改变后的结果。红黑树的核心是左旋
跟右旋
。
左旋跟右旋
左倾红黑树的插入一共有三种可能的情况。
插入20
插入14
插入17 整体来说左倾红黑树的插入就是这3种情况来回切换,最终达到平衡。
删除思路是不删除目标数据,而是找到目标数据的前驱节点或后继节点,然后把数据拷贝一份到目标数据进行覆盖。然后转而去删除前驱或后继。删除后再去修补平衡。
从宏观上来看从根节点开始查找,全程利用2-3树思维逐层对红黑树调整,每次保证当前节点树2-3树中非2节点,如果是非2节点则看下一层,如果是2节点则根据兄弟节点调整。
删除目标1
删除后就涉及到数据平衡修复了,还是根据2-3树来修复平衡,路上可能会碰到红色右倾节点,遇到就进行一次左旋即可。
2-3树修补工作
这里其实主要参考极客时间小争哥的文章,说下实际工程中红黑树的增删操作,增加主要有3种情况:
情况1
情况1:关注节点是 a,它的叔叔节点 d 是红色:
情况2 情况2:关注节点是 a,它的叔叔节点 d 是黑色,关注节点 a 是其父节点 b 的右子节点:
情况3
情况3:如果关注节点是 a,它的叔叔节点 d 是黑色,关注节点 a 是其父节点 b 的左子节点,我们就依次执行下面的操作:
相比插入,删除就难多了!核心思想是找准关注点
,根据关注点跟周围节点排布特征按照一定规则调整。主要俩步骤:
算法导论中说如果删除黑节点X带来黑色平衡破坏,让X的子节点变为黑-黑
或红-黑
。意思是既然删除了某个黑色节点,那么必然会破坏以这个黑色节点为路径上的黑色平衡,表现为路径中缺少一个黑,所以要想办法补充一个黑色节点(下面会用黑色圆圈表示)。同时如果一个节点既可以红又可以黑,就用红黑两个组成部分表示。
情况1 情况1:要删除的节点是 a,它只有一个子节点 b:
情况2 情况2:要删除的节点 a 有两个非空子节点,并且它的后继节点就是节点 a 的右子节点 c:
红 - 黑
或者黑 - 黑
。情况3 情况3:要删除的是节点 a,它有两个非空子节点,并且节点 a 的后继节点不是a的右子节点:
红 - 黑
或者黑 - 黑
。经过初步调整之后,关注节点变成了红 - 黑
或者黑 - 黑
节点。针对这个关注节点,再分四种情况来进行二次调整。二次调整是为了让红黑树中不存在相邻的红色节点。
情况1 情况1:关注节点是 a,它的兄弟节点 c 是红色的,我们就依次进行下面的操作:
情况2 情况2:关注节点是 a,它的兄弟节点 c 是黑色,并且节点 c 的左右子节点 d、e 都是黑色:
红 - 黑
或者黑 - 黑
情况3
情况3:关注节点是 a,它的兄弟节点 c 是黑色,c 的左子节点 d 是红色,c 的右子节点 e 是黑色:
情况4
情况4:关注节点 a 的兄弟节点 c 是黑色的,并且 c 的右子节点是红色的,我们就依次进行下面的操作:
递推
,比如插入情况1,情况1的处理之后,关注节点从本身变成了它的祖父红色节点,这就是往根节点递推。不过情况1处理过一次之后,不一定会进入情况2或者情况3,有可能还在情况1。在情况1的情况下一直往根节点走,因为当前节点永远是红色,所以在最后要把根节点涂黑。同时只要进入到情况2、情况3情况,操作就跟上面说过的类似了。红-黑
跟黑-黑
存在的原因,为何最终都会走到从兄弟节点的地方做文章来实现最终的平衡。本文的重点不在于讲解工业化红黑树的删除跟插入全部过程,只是希望通过2-3树跟左倾红黑树的增删,让大家从本质上理解下红黑树的操作来源。其中工业化删除部分已征得小争哥同意,如果理解了上面的内容,那么你再去看工业化红黑树的操作就手到擒来了。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有