数据结构 - 学习笔记 - 红黑树前传——234树
简介
234树——234Trees
在学习红黑树
前需要先了解234树
。因为红黑树
就是由234树
演变出来的。(红黑树是234树的一种实现)
了解了234树
才能明白红黑树
旋转和颜色变化的底层逻辑。
结点类型
结点 | 元素个数 | 子结点个数 子结点值域 | 对应红黑树结点 |
---|---|---|---|
2结点 | 1个元素。 如: a | 2 个子结点或无 子结点( , a) , (a, ) | 黑色a |
3结点 | 2个元素。 如: a < b | 3 个子结点或无 子结点( , a) , (a, b) , (b, ) | 父结点必需是黑色且有2种情况 1. 红色a ←左子— 黑色b 2. 黑色a —右子→ 红色b |
4结点 | 3个元素。 如: a < b < c | 4 个子结点或无 子结点( , a) , (a, b) , (b, c) , (c, ) | ┌──黑色b──┐ 红色a 红色c |
红黑树对应关系图示
- 上图中没有画出具体的
子结点
仅用虚线框标识了一下子结点的取值范围。 - 不难看出,只要将
红黑树
中的红结点向上一提,与父结点合并,就反推出了234树
。 - 同时根据
3结点
的特性,可以看出234树
与红黑树
是一对多
的关系。 - 在平时分析
红黑树
时,可以直接在脑海里映射成234
树来分析。这样变化
,旋转
操作就有据可依,而不是死记硬背了。
插入
插入规则
插入场景 | 插入前 | 插入示例值 | 插入后 | 结论 |
---|---|---|---|---|
当前无结点 | 空 | 5 | [⑤] | 直接插入就完事了。 |
当前2结点 | [5] | 3 | [③, ⑤] | 2结点 升级为3结点 。 |
当前3结点 | [3, 5] | 7 | [③, ⑤, ⑦] | 3结点 升级为4结点 。 |
当前4结点 | [3, 5, 7] | 8 | [⑤] [③], [⑦, ⑧] | 4结点 插入新值时,会触发分裂。1. 中间值 ⑤ 上升,与父结点合并。 2. 找到 ⑧ 的插入位置。 插入后与⑦合并得到 [⑦, ⑧]。 |
插入步骤整体演示
先看一下演示,后面还有对 2, 3, 4 结点的分别说明。
下图演示了234树
的插入步骤,以及触发分裂
的效果。(新插入的元素,按红黑树规则标为红色)
下图对应234树
插入步骤,在右侧列出对应的红黑树
。因为3结点
转红黑树
存在两种情况,所以按排列组合方式一一列出。
对照前文的与红黑树对应关系
,可以看到黑红
颜色背后的逻辑来源于234树
。
2, 3, 4 结点分别说明
1. 在2结点插入
2结点
插入新值,直接升级为3结点
,无需任何调整。
2. 在3结点插入(红黑树旋转+变色)
3结点
插入新值,直接升级为4结点
,无需任何调整。
但在3结点
对应的红黑树
中,可能出现不平衡的情况。需要旋转调整实现平衡。
- 旋转方向的逻辑可以想象天平。
左边重
了往右挪(旋)
,右边重
了往左挪(旋)
。 - 从上图可以看到有
3
个位置能插入子结点
。对应6
种红黑树的情形。 - 其中有
2
种是平衡的,无需调整。剩下4种
需要通过旋转
+变色
实现平衡。
3. 在4结点插入(红黑树变色)
3.1. 从234树→红黑树
先简单的把 4结点
与 红黑树
对应关系,罗列出来。根据新结点插入的位置不同,对应的红黑树也有所差异:
对应上图的步骤序号。
- 原234树为 ③⑤⑦。
- 插入新元素。将要触发分裂。
- 先分裂。然后按新值大小,找到插入位置,与原有的
2结点
合并成为3结点
3.1. 中值上提为父结点,如果原本有父结点,分裂出去的结点,与原父结点合并。
3.2. 如果原来的父结点也是一个4结点
,将递归
触发分裂
。
3.3. 剩下两个结点,小的为左,大的为右。 234树
结点转红黑树
结点。
4.1. 所有2结点
变为黑色。
4.2.3结点
展开,上黑下红。
3.2. 触发分裂
单独分析一下触发分裂效果。
- 从左向右:演示了从
234树
的角度来染色的逻辑。 - 从右向左:演示了从
红黑树
的角度来染色的公式
。 - 如果当前操作的只是一颗
子树
。比如祖父
结点②也是红色,则出出了②⑤两个连续的红结点,需要递归处理,直至根。
3.3. 对应红黑树有4种情形需要变色实现平衡
- 【234树】 插入新元素。将要触发分裂。
- 【234树】 先分裂。然后找到待插入的目标位置,与原有的
2结点
合并成为3结点
- 【红黑树】 这是一个过度状态,为了便于观察才把它画出来。(
3结点
展开,但未调色,暂时还保持着不平衡的状态,便于观察) - 【红黑树】
父
和叔伯
结点变黑,祖父
结点变红。
4.1.父
结点与祖父
结点调换颜色:满足红结点子必黑的定义。同时对于插入新结点的这一路径来说黑结点数未发生变化。
4.2.叔伯
结点变成黑色:祖父
结点原本作为公共的黑结点,挪给左路后,右路就少了一个黑结点。因此叔伯
要站出来变黑维持平衡。 - 【红黑树】 最后
祖父
结点更新为当前结点。
5.1. 判断曾祖
是否为红色。如果是,则需要向上递归调整颜色,一直到根。
5.2. 如果是根,直接染黑。
删除
删除操作:根据删除目标所在位置不同,共有几种情况需要分别处理。
删除规则
1. 删除目标有子结点
- 找到
目标
的前驱
或后继
结点。 - 将
目标
与前驱
或后继
交换。 - 交换后
目标
转变成了无子结点,按无子结点删除规则处理。
2. 删除目标无子结点
目标
在4结点中,删除后变成3结点。目标
在3结点中,删除后变成3结点。目标
是2结点,有三种情况:
3.1. 兄弟是3结点
或4结点
。
3.2. 两个兄弟结点都是2结点
,但父结点
是3结点
或4结点
。
3.3.兄弟
和父
结点都是2结点
。
2, 3, 4 结点分别说明
1. 在4结点删除
删除一个元素,变成3结点
。保持平衡。
4结点
的删除,如果忽略掉它的兄弟结点。本质上还是一个3结点
的删除。
对应红黑树:
- 红结点:直接删除即可。
- 黑结点:删除黑结点,红结点补位,并变成黑色。
234树
结点,蓝色 标出是要删除的目标。- 转为对应的
红黑树
。 - 删除目标结点。
- 如果删的是
父结点
。子结点
上移补位。 - 补上来的结点染成原
父结点
的颜色。如果是根结点
直接填充黑色
。(虽然单看234树转过来的这个局部,父结点必定是黑色。但在一个完整红黑中,父结点有可能是红色)
2. 在3结点删除
删除一个元素,变成2结点
。保持平衡。
- 红结点:直接删除即可。
- 黑结点:删除黑结点,红结点补位,并变成黑色。
原234树
结点,蓝色 标出是要删除的目标。- 转为对应的
红黑树
。 - 删除目标结点。
- 如果删的是
父结点
。子结点
上移补位。 - 补上来的结点染成原
父结点
的颜色。如果是根结点
直接填充黑色
。
3. 在2结点删除
删除结点后,当前位置空出,红黑树失去平衡。需要向父兄
结点借元素借兵补充。
3.1. 兄弟是3或4结点
- 通过从兄弟节点借调元素,将
2节点
转变为3节点
。(通过左旋
或右旋
实现转变。) - 然后按
2、3节点
规则删除目标。
3.2. 兄弟都是2结点,父是3或4结点
- 向父结点借来 ④,合并出新的结点 [③, ④, ⑥]。
- 按
4结点
规则删掉目标。
3.3. 父兄结点都是2结点
- 向父借来元素,合并出新的
4结点
。 - 按
4结点
规则删掉目标。
如果当前是子树,则父结点需要递归
向上借调。
总结
- 可以将
红黑树
看作是234树
的一个具体实现。 - 一颗
234树
可以对应多个红黑树
。(因为3结点
对应红黑树
时可以左倾,也可以右倾) - 明明是包含
123
个元素的结点,为什么不叫123树
而叫234
树。我的理解是:因为树的特性就是分叉
,结点的命名是按它的分叉能力来的(而不是按结点包含的元素个数)。n结点
就是能分n个叉
的结点。(吐槽:真是跟制动
有异曲同工之妙。但是二叉树
,二叉树
叫的又那么顺口,好吧,当我没说。) 234树
就是4阶B树
(four-order balance tree)。也就是允许结点
最多有4个子结点
的平衡多路
查找树。234树
是倒着长的。新元素插入到叶子
,然后触发分裂
向上提升元素
成为父结点。
5.1. 如果原本有父结点,则与父结点合并。
5.2. 如果满足条件还会递归触发分裂
,前面 插入-触发分裂 有详细讲。