红黑树详解
红黑树
红黑树性质
- 每个节点要么是黑色,要么是红色;
- 根节点是黑色;
- 每个叶子节点是黑色;
- 每个红色节点的两个子节点一定是黑色;
不能有两个红色节点相连;- 任意一节点到每个叶子节点的路径都包含数量相同的黑节点;
如果一个节点存在黑子节点,那么该结点肯定有两个子节点;
看看下面三个例子
例一,显然不满足根节点是黑色
例二,将例一中所有节点的红黑颜色反转,现在就没有问题了
例三,他不满足红黑树的点是55节点,以55节点为根节点的红黑树不满足“黑高”性质,他只有左节点为黑色,没有右节点
红黑树的效率
查找:红黑树是平衡二叉树,满足二叉树的性质,同时红黑树自平衡,在插入/删除时都会执行平衡操作以保证树高,所以搜索效率是O(logn)
插入/删除:平均每次都会执行旋转和变色操作,但正是因为旋转变色以自平衡保证树高,所以插入删除也只需要执行O(logn)的搜索然后进行O(1)的其他操作,平均还是O(logn)
注意:正是因为有子平衡操作,即使按顺序插入节点,红黑树也不会退化成链表
红黑树的等价变化
首先看下面这个红黑树,他满足所有的五条规则
如果将上面图片中所有的红节点与他的父节点合并,可以得到下面图片
然后将上面的多节点合并成一个节点,就可以得到下面的四叉树,四叉B树,也叫2-3-4树
由此,可以得出下面结论
- 红黑树与四阶B树具有等价性;
- 黑色节点与他的红色节点融合在一起,形成一个B树节点;
- 红黑树的黑色节点个数与四阶B树的节点总个数相等,因为所有红色节点合并到黑色节点中了;
- 在所有的B树节点中,永远是黑色节点是父节点,红色节点是子节点,黑色节点在中间,红色节点在两边;
本文后面的图片中所绘制的红黑树都是按照B树绘制的
红黑树的操作
旋转操作
左旋
下图中,对P节点进行左旋,就是将P节点转到下边,P节点的右子节点V节点转到上边,P节点值小于V节点,成为V节点新的左子节点,V节点原本的左子节点R大于P节点,成为P节点新的右子节点
右旋
对P节点执行右旋,P节点转到下边去,P节点的左子节点F节点转上来。P节点成为F新的右子节点,F节点原本的右子节点K成为P的新左子节点
红黑树添加
- 新插入的节点是红色的。如果插入黑色节点,一定会打破红黑树那一条分支的平衡,再调整开销会很大
问题分析
- 黄色提到的问题,以最左侧红黑红节点为例,因为红色节点没有权利新开一个节点或新开一层,所以新节点应该和46节点在一起,导致溢出;
- 红色提到的问题,在黑红或者红黑场景下,新插入一个节点不会导致溢出,但如果这个节点是插在红色节点下的,会导致红色和红色相连;
- 绿色的,在黑色节点下插入一个红色节点并且没有导致溢出,是完全没有问题的;
插入问题的处理
1、LL和RR,红红相连但无溢出
以RR为例
- 刚插入85的时候,是
76->82->85
,然后82和85红红相连,一个平衡的“三节点”应该是红黑红,同时大小关系也是红<黑<红;- 即82应该成为中间的节点,同时82变成黑色,76变成红色,76和85分别做82的左右子节点,最后让55的箭头指向82,也就是变色之后对76执行一次左旋;
2、LR和RL,红红相连无溢出
以RL为例
从四叉树的角度来看,和上面的RR很类似,刚插入80时是
76->82->80
,82和80红红相连,所以最终的结果还是76、80、82这三个数中,中间的放在中间,变成黑色,另外两个为红色,所以最终是80在中间,改成黑色,76和82分别做左右子节点,都是红色;具体操作,首先对82执行一次右旋,就变成了RR双红的情况;
再对76执行一次左旋;
3、插入导致B树节点上溢出
在一个红黑红的满载节点上,新插入一个红色节点,导致该节点超载;
首先,对“红黑红”节点进行染色,将46改成红色,将40和50改成黑色,之后将红色节点向上提到父节点那一层;
现在从46节点出发,下面的节点都符合条件了;
红色节点向上提之后,将这个红色节点视为一个新插入的节点,考虑将他插入到父节点中,然后继续操作,即递归判断是否符合条件;
观察发现,现在46的父节点也是红黑红的满载状态,46的加入使其超载,刚好和20节点的插入一样,所以递归执行一样的操作,将88节点染红,55和100染黑,然后将88节点上提;
最后,继续对上提的88节点判断,发现现在88节点作为根节点,所以88需要再次染黑;
红黑树删除
前言
普通二叉树的删除
底层节点的删除
直接删除,没有影响
非底层节点
找左子树最大或右子树最小来代替自己,最后删除底层节点
所以发现,二叉树的删除其实都发生在底层
最后需要明确,红黑树的删除,第一步也是将对于非底层节点的删除进行转换,将其与其前驱(左子树最大节点)或者后继(右子树最小节点)进行交换,此时就会变成删除底层节点或者删除一个只有一个子节点的节点
交换后遇到红节点
交换后如果遇到红节点,那么这个红节点应该是底层节点或者有一个黑色子节点的节点
如果是底层节点,因为同时又是红色节点,删除对红黑树没有任何影响,可以直接删除
如果是有一个黑色子节点的红节点,其实也和底层节点没什么区别了,因为太简单了,删除掉他之后让他的黑色子节点顶替他的位置,依旧对红黑树没有任何影响
交换后遇到黑节点
如果被删除的节点变成了黑节点,情况就复杂很多
- 被删除的节点有两个红色子节点;
- 被删除的节点有一个红色子节点;
- 被删除的节点没有子节点;
删除有红色子节点的黑节点
对于前两中情况,都只需要与他的一个红色子节点交换,然后删除底层的红色子节点即可
删除没有子节点的黑节点
继续划分情况,接下来要考虑被删除的黑色叶子节点的兄弟节点
删除黑色叶子节点,且其兄弟节点为黑色
接下来就是考虑兄弟节点
如果兄弟节点至少有一个红色子节点,如在下面三种情况中删除105,这里的关键是向兄弟借
对于情况一
- 删除105节点,平衡被打破,100节点有一个黑色左子节点,但是没有右子节点;
- 这里很容易看出来,只需要执行一次旋转操作即可,对100执行一次右旋,将95提拔上来,然后染色;
对于情况二
- 删除105节点,平衡被破坏,100节点有黑色左子节点但是没有右子节点;
- 此时是LR,可以通过对95执行一次左旋,再对100执行一次右旋完成调整;
- 其实在对100完成右旋的时候,应该是98为黑色,95和100为红色,88和98同为黑色且在同一层,改变一下颜色更好;
对于情况三
- 兄弟节点95只需要任意借一个子节点给105都可;
- 如果借98,是LR型,需要左旋一次再右旋一次,如果借90,是LL型,只需要右旋一次,显然选择更简单的90;
第四种情况,如果兄弟也一个红色子节点都没有,那就需要父节点向下与兄弟节点合并为新的b树节点来修复整个b树的平衡了,父节点向下被称为“下溢”,下溢也会带来连锁反应
- 删除105后,发现兄弟节点95也是光杆司令,没有红色子节点;
- 100下调到95这一层,现在100是红色节点,有一个黑色左子节点,没有右子节点,所以95和100颜色对调;
第五种情况,在第四种情况的基础上,兄弟没有红色子节点,同时父节点是黑色
- 删除60节点,60节点的父节点为黑色节点,同时兄弟节点没有红色子节点,如果只是按照第四种情况的方式来解决,会导致黑高不对;
- 首先,将兄弟节点70染成红色,然后让父节点下调,此时父节点之前的位置可以想象成删除了一个节点,此时就会发现,被删除节点和40、50所形成的局面和第四种情况一样;
- 所以接下来只需要40染红,50染黑,然后将50下调就可以了;
删除黑色叶子节点,且其兄弟节点为红色
- 下面树中,要删除100,发现100的兄弟节点88为红色;
- 将兄弟节点88染黑,将父节点95染红,再对父节点95执行一次右旋,得到第二棵树,发现此时删除100就和前面的情况四一样了;
- 要删除的节点的兄弟节点为黑节点且没有红色子节点,父节点也是红色节点,此时将兄弟节点90染红,将父节点95染黑,并将父节点下调;
案例
最后看两个复杂案例
第一个,删除56节点
- 56节点父节点为黑色,兄弟节点为没有红色子节点的黑色节点,第一步兄弟染红,父节点55下调;
- 此时被删除节点、80、88形成的局面是,兄弟节点为红色,所以父节点80染红,兄弟节点88染黑,然后对父节点80执行一次左旋,使得被删除节点的兄弟节点为黑色,父节点为红色;
- 最后兄弟节点76染红,父节点80染黑,然后父节点下调,结束;
第二个,全都是黑色节点,删除25
- 父节点为黑色,兄弟节点黑色,且兄弟节点没有红色子节点,兄弟染红,父节点下调;
- 第二棵树种,被删除节点、40、50所形成的仍是父节点黑色,兄弟黑色无红色子节点,所以再来一次,兄弟染红,父节点下调,然后就发现结束了;
参考视频
B站——小刘讲源码