红黑树定义
- 每个节点要么红要么黑
- 根节点是黑色
- 每个叶子节点都是黑色(叶子是NULL节点表示 或 NIL节点表示)。
- 如果一个节点是红色的,那么它的子节点必须是黑色的子节点。(即不存在两个连续的红色节点)
- 从任一节点到其每个叶子结点(NULL节点或 NIL节点)的所有简单路径都包含相同数目的黑色节点。
红黑树的插入算法分析
- 【约定一:新增节点一定是红色节点】。若是黑色节点,每次新增黑子叶子节点,根到叶子节点的路径上黑节点多1个黑色节点,违背了性质5;如下图所示:
若插入红色节点,只需要调整父结点为红色情况,即连续两个节点为红色的情况
,若插入黑色节点,所有情况都需要调整
。
- 【约定二:调整需要旋转+节点变换颜色】。当出现两个出现两个连续红色节点,则不满足红黑树性质,需要使用两种手法调整结点颜色或位置,来保持树满足红黑树
节点变换颜色算法
变化颜色比较简单,红->黑、黑->红、指定某一个结点为黑、指定某一个结点为红。共四种情况
旋转算法
旋转算法包括左旋和右旋,两种属于镜像对称实现。旋转都是调整结点的位置
左旋
- x右孩子指向y的左孩子
- y的左孩子指向x
右旋
- y左孩子指向x的右孩子
- x的右孩子指向y
- 【约定三:哪些情况需要调整】。当出现两个出现两个连续红色节点,需要调整。假设原红黑树为T,现新增一个红色节点new后,导致原红黑树为T遭到破坏,设树变成T1(此时不满足红黑树)。我们可以得到以下两点重要特征
- 根据T1,我们可以得知红色节点new父结点parent结点,一定是红色(两个连续红色节点)。
- 根据T,我们可以得知父结点parent结点的父节点grandpa,即new祖父结点grandpa一定是黑色,T是一个红黑树(不存在两个连续红色节点)。
如图所示,需要调整如下,简单分为CASE A/CASE B两大类型,两种是对称的。他们的区别是父节点P的位置,如果节点P是左孩子,即为CASE A;如果节点P是右孩子,即为CASE B
红黑树的插入调整CaseA分析
CaseA1:当前节点的父节点是红色,且当前节点的父节点的另一个节点(叔叔节点)也是红色。这是不关心当前节点位置
调整策略
- 父节点设置黑色
- 叔叔节点设置黑色
- 祖父节点设置红色
- 设置祖父节点为当前节点(祖父结点变红,可能祖父结点的父结点也是红色,出现连续两个红色节点,所以祖父结点也要判断一次)
CaseA2:当前节点的父节点是红色,叔叔节点是黑色且当前节点是父节点的右孩子。
调整策略
- 将父节点作为新的当前节点
- 以当前节点为支点左旋
CaseA3:当前节点的父节点是红色,叔叔节点是黑色且当前节点是父节点的左孩子。
调整策略
- 设置父节点为黑色
- 设置祖父节点为红色
- 以祖父节点为支点右旋
红黑树的插入调整CaseB分析
CaseB1:当前节点的父节点是红色,且当前节点的父节点的另一个节点(叔叔节点)也是红色。这是不关心当前节点位置
(CaseB1与CaseA1处理逻辑相同)
调整策略
- 父节点设置黑色
- 叔叔节点设置黑色
- 祖父节点设置红色
- 设置祖父节点为当前节点(祖父结点变红,可能祖父结点的父结点也是红色,出现连续两个红色节点,所以祖父结点也要判断一次)
CaseB2:当前节点的父节点是红色,叔叔节点是黑色且当前节点是父节点的左孩子。
调整策略
- 将父节点作为新的当前节点
- 以当前节点为支点左旋
CaseB3:当前节点的父节点是红色,叔叔节点是黑色且当前节点是父节点的右孩子。
调整策略
- 设置父节点为黑色
- 设置祖父节点为红色
- 以祖父节点为支点左旋
插入调整Case汇总
第一种调整:CASEA1 + CASEB1,算法一致。新增节点可出现在下列四个任意位置
第二种调整:CASEA2 + CASEA3,若出现CASEA2 ,先调整为CASEA3,再最终调整为红黑树子树
第三种调整:CASEB2 + CASEB3,若出现CASEB2 ,先调整为CASEB3,再最终调整为红黑树子树
针对三种总结,我们进行代码实现。参考:https://blog.csdn.net/dengjili/article/details/113204943