在上一篇我们说到2-3树在某些情况插入后的平衡操作可能会使得效率降低,在2-3查找树基础上改进的红黑树不仅具有较高的效率。那么红黑树是如何操作的呢?
定义
红黑树是一种近似平衡的二叉查找树,它能够确保任何一个节点的左右子树的高度差不会超过二者中较低那个的一陪。具体来说,红黑树是满足如下条件的二叉查找树(binary search tree):
- 每个节点要么是黑色要么是红色
- 根节点是黑色
- 每个叶子节点是黑色,并且为空节点(还有另外一种说法就是,每个叶子结点都带有两个空的黑色结点(被称为黑哨兵),如果一个结点n的只有一个左孩子,那么n的右孩子是一个黑哨兵;如果结点n只有一个右孩子,那么n的左孩子是一个黑哨兵。)
- 如果一个节点是红色,则它的子节点必须是黑色
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
特性3中指定红黑树的每个叶子节点都是空节点,但是在Java实现中红黑树将使用null代表空节点,因此遍历红黑树时看不到黑色的叶子节点,反而见到的叶子节点是红色的;
特性4保证了从根节点到叶子节点的最长路径的长度不会超过任何其他路径的两倍,例如黑色高度为3的红黑树,其最短路径(路径指的是根节点到叶子节点)是2(黑节点-黑节点-黑节点),其最长路径为4(黑节点-红节点-黑节点-红节点-黑节点)
在树的结构发生改变时(插入或者删除操作),往往会破坏上述条件,需要通过调整使得查找树重新满足红黑树的条件
说到当查找树的结构发生改变时,红黑树的条件可能被破坏,需要通过调整使得查找树重新满足红黑树的条件。调整可以分为两类:一类是颜色调整,即改变某个节点的颜色;另一类是结构调整,即改变检索树的结构关系。
结构调整过程包含两个基本操作:左旋(Rotate Left),右旋(RotateRight)。
变色
如果当前节点的父亲节点和叔叔节点均是红色,那么执行以下变色操作:
父 --> 黑
叔 --> 黑
爷 --> 红
开始分析爷爷是否满足红黑树特性
左旋
左旋的过程是将x的右子树绕x逆时针旋转,使得x的右子树成为x的父亲,同时修改相关节点的引用。旋转之后,二叉查找树的属性仍然满足。
触发条件:
- 父亲是红色
- 叔叔是黑色
- 当前是右子树
执行:按照依父亲为中心进行旋转
右旋
右旋的过程是将x的左子树绕x顺时针旋转,使得x的左子树成为x的父亲,同时修改相关节点的引用。旋转之后,二叉查找树的属性仍然满足。
右旋转条件:
- 父亲是红色
- 叔叔是黑色
- 当前是左子树
执行:
- 父亲节点变为黑色
- 爷爷节点变为红色
- 再依爷爷节点为中心进行旋转
案例
下面已一个具体的实例进行分析红黑树的执行过程,如下图,新插入一个节点25,因为所有新插入节点均为红色,不满足条件:不能有连续的红色,且当前节点的父节点28和叔叔节点36均为红色,则需要进行变色,如下图:
变色后,此时20节点和30节点不满足”不能有连续的红色“条件,又因为30节点的父亲20是红色,叔叔50是黑色,且当前是右子树,依父节点20为中心进行左旋,如下图:
左旋后,此时20和30节点还是不满足”不能有连续的红色“,20的父亲30节点是红色,叔叔50是黑色,且在左子树,需要先将父亲30节点变黑色,将爷爷界定啊39变红色,依爷爷39节点为中心右旋,如下图:
这样红黑树就完成了。
本文参考资料如下:
JAVA学习-红黑树详解
Java 红黑树(Red-Black tree)
7张图带你了解红黑树变色、左旋和右旋
(图文结合)详细描述红黑树如何左旋、右旋