数据结构学习记录DAY12(大概) :红黑树 RBtree
-
平衡二叉树的左右高度差不能超过一,这个条件过于苛刻。
-
红黑树(大致平衡的二叉排序树)的特点
-
最长路径<2*最短路径,放松了对平衡的限制,但是能确保杀入删除查找的时间复杂度在Olog2n
我们不是从零创造红黑树,我们是通过学习既有模型了解性质加以使用,因此我们用一个经典模型展示借以描述红黑树的特点。
-
由此可以引出红黑树的五条规则:
- 每一结点非红即黑
- 根结点必定是黑色
- 一条路径上的红色节点不能连续(黑色可以)
- 所有的叶子结点视为null都为黑色
- 从任意一个结点出发,到其所有叶子结点路径上的黑色结点的数量必须相等
有这五条规则,就可以开始写一棵红黑树了(大雾
其实是就可以开始探究红黑树的插入和删除了,因为红黑树的其他操作和平衡二叉排序树是一样的。
但是由于红黑树加入了限制,所以在插入和删除的操作上会相当复杂。
-
红黑树的插入
- 插入规则:每次插入都是红色的(若插入的位置是一个红色的结点,则会破坏红红的规则)
- 为什么会是这样的插入规则呢?因为从根结点出发,到叶子结点如果有m条路径,插入之后在其中一条路径上增加一个黑色结点,剩下的m-1条路径都无法使其增加黑色的结点。相较于对m-1条路径进行操作,破坏红-红规则带来的后果显然会好处理很多
- 插入需要修复的情况:
-
变色:有叔叔结点,则向上变色(插入在左子树或右子树效果相同
--
父结点和叔叔结点都为红色的,则父叔染黑爷爷染红
-
对爷爷结点向上讨论,若爷爷为根则变黑
- -
网上找相关内容的时候也看见有说会把新结点染黑再将父节点染红,个人觉得可以有,但不是非得有
-
-
旋转:没有叔叔结点,旋转后变色
-
RR型 : 父在爷爷右边,当前结点在爷爷右边
-- 对爷左旋,原来的父染黑爷染红,
- 对爷左旋,原来的父染黑爷染红,
-
LL型 : 父在爷爷左边,当前结点在爷爷左边
- (与RR型对称,就不图示了)对爷右旋,父黑爷红,
-
LR型 : 子在爷爷左边,又在父亲右边
-- 对父左旋, 对爷右旋,爷染红子染黑
-
- 对父左旋, 对爷右旋,爷染红子染黑
-
RL型 : 子在爷爷右边,又在父亲左边
- 对父右旋, 对爷左旋,爷红子黑
-
-
最多需要两次旋转,和log2n次 的颜色变化。
-
- 插入规则:每次插入都是红色的(若插入的位置是一个红色的结点,则会破坏红红的规则)
-
红黑树的删除
- 删除的结点没有子节点
- 删除的结点为黑色:需要进一步考虑修复平衡
- 红色:直接删除即可
- 删除的结点只有一个子节点
- 其子结点一定是红色的,用红色子结点替换要删除的结点,然后把那个结点染黑;
- 删除的结点有两个子节点
- 找左子树最大值替换当前元素
- 转换成以上两种情况
- 删除的结点没有子节点
-
修复需要考虑的
-
删除了黑色结点 到这条路径上的黑色节点少了一个,解决冲突在于能否找到一个红色结点经过旋转 染色 把它替换被删除的黑色的结点
-
有以下几种情况:
- 兄弟结点为红色,父亲和以及侄子都为黑色
- 如下图删除结点:7
-
1、先删除该节点(路径上的黑少了1,需要修补)
2、兄弟结点左旋
-
3、兄弟结点染成原本父亲结点的黑色,父亲结点染红
4、对原来的父亲结点进行左旋
5、染色,结束
- 兄弟结点为黑色:
- 兄弟的孩子为黑
* 若父结点为红 父染黑 兄弟染红即可 结束
详细情况如图中删除70号
1、先寻找左子树中最大的元素,替换要删除的元素,如左子树中最大元素为红色的,则完成,若为黑色就看例子2
删除75:
1、取左子树中最大值替换
2、替换后乍看好像完美了,但是有一条规则“所有的叶子结点视为null都为黑色”
所以此时不满足,需要将78结点置为红
3、将兄弟节点50染红,父亲结点61染黑即可
* 若父结点为黑 兄弟染红 以父结点当作删除的结点继续向上讨论
- 兄弟的孩子不全为黑
- 远端侄子为黑 (近端为红) 近端侄子染黑 兄弟染红 对兄弟进行旋转,使得远端侄子为红
图解(以删除28号结点为例):
1、没有子树就直接干掉,有就从左子树找最大值,或者直接将右子树接上
2、兄弟左旋
3、近端侄子染黑
4、此处情况特殊,需要右旋25,然后染黑20
- 远端侄子为红 对父结点进行旋转 兄弟结点置成父结点的颜色 远端侄子和父结点置黑即可
- 图解(以删除25号结点为例):
1、左子树最大值替换
2、右孩子置为红
3、父结点左旋
4、兄弟结点置成父结点的颜色(黑色) 远端侄子(75)和父结点置黑即可
总结
-
插入
- 插入的结点为红色
- 插入需要解决 红+红
- 通过染色 父 + 叔 为红 父和叔染黑 祖父染红 继续向上讨论
- 通过旋转 叔为黑
- 自己 和 父 处于同一边 再通过祖父旋转,让祖父(黑色的结点)到另外一边,少了一个黑色结点 把父结点变成黑色的 到达插入位置结点黑色的结点不变, 另外一边由于多了一个黑色结点(父结点旋转为公共路径上的结点) 把祖父结点(旋转到另外一边)置红,用于抵消公共路径上黑色结点的增加
-
删除
- 只有删除黑色叶子结点 修复
- 因为删除黑色结点 导致到该路径上的黑色结点减少
- 借 只有当有红色结点时才有可能借 必须是远端侄子为红
- 兄弟为红(侄子为黑) 通过旋转 让近端侄子变为兄弟
- 只有兄弟为黑,侄子才有可能为红
- 只有当远端侄子为红时,才能过旋转,让兄弟结点到公共的路径上(兄弟黑的,弥补删除时删除了一个黑色的结点),删除结点的那边会少出一个黑色结点 ,让远端红色的侄子变成黑色的 (需要注意的是兄弟结点需要染成原来父结点的颜色,在实际中,父结点染黑,远端侄子染黑)
- 一水黑 (全都为黑) 使左右各少一个黑色结点 再对父结点向上讨论
- 借 只有当有红色结点时才有可能借 必须是远端侄子为红
红黑树的应用
- C++ map/set/multiset/multimap 底层的实现就是红黑树
- JAVA TreeSet/TreeMap 底层实现也是红黑树
- 进程管理 Task任务用红黑树存储的
- 数据库 索引 红黑树实现的
- 多路复用IO epoll
以上!
希望能给你一点帮助