红黑树是一种特殊的二叉树,并且是优秀的自平衡查找树,下图为红黑树的示例:
红黑树具有以下几大特性:
1、根节点为黑色。
2、所有节点都是黑色或红色。
3、所有叶子节点(Null)都是黑色。
4、红色节点的子节点一定是黑色的。
5、任意一个节点到其叶子节点的所有路径上的黑色节点数量相同(黑色完美平衡二叉树)。
以上的五大特定也是维持红黑树结构的基本规则,但是明白了这些规则,不代表我们就明白了红黑树的设计原理及规则维持算法。
在我们日常的工作中多多少少都会接触到红黑树,特别是JDK1.8之后hashmap的底层采用了红黑树机构,接下来的博文中我们会一点点弄明白以下几个问题,也是笔者在学习之前不明白的两个问题:
1、红黑树为什么要维持自平衡、自平衡的好处是什么?
2、什么是左旋,右旋,变色?
3、什么条件下需要进行左旋、右旋、变色?
二、红黑树的自平衡
根据上一节红黑树特性第5点可以知道,红黑树是一颗黑色完美平衡二叉树,红黑树从根节点到叶子结点的最长路径不会超过最短路径的2倍;
这就保证了红黑树优秀的查找性能,其查找的时间复杂度为O(logn);
当插入或删除阶段操作过程中,会破坏此平衡结构,当平衡遭到破坏,程序会进行一系列操作来重新维持平衡,这一过程就是自平衡;这一系列操作就是左旋、右旋、变色。
1、左旋:
对当前节点进行左旋:当前节点的右子节点变为父节点,原右子节点的左子节点变为当前节点的右子节点,如图演示(对150节点进行左旋):
上图对150节点左旋:
把150节点的右子节点170变为其父节点(170变为150的父节点)。
把150节点的原右子节点170的左子节点160变为其右子节点(160变为150的右子节点)。
注意:
图中只是示意图,在第一步之后,170节点并没有三个指向其他节点的指针,这里只是为了理解起来更清晰
图中只是进行左旋,还没有涉及变色问题,所以会看到父节点与子节点同色问题。
左旋操作涉及到的节点只是当前节点的右子节点和右子节点的左子节点两个节点,其他节点不变。
2、右旋:
对当前节点进行右旋:当前节点的左子节点变为父节点,原左子节点的右子节点变为当前节点的左子节点,如图演示(对150节点进行右旋):
上图对150节点进行右旋:
把150节点的左子节点130变为其父节点(130变为150的父节点)。
把150节点的原左子节点130的右子节点null节点变为其左子节点(130的原右子节点null变为150的左子节点)。
注意:
图中只是示意图,在第一步之后,130节点并没有三个指向其他节点的指针,这里只是为了理解起来更清晰
图中只是进行左旋,还没有涉及变色问题,所以会看到父节点与子节点同色问题。
右旋操作涉及到的节点只是当前节点的左子节点和左子节点的右子节点两个节点,其他节点不变。
3、变色:
节点的变色:就是节点由红色变成黑色或有黑色变成红色。
而在实际的维持自平衡的过程中变色过程有可能是一个连锁反应,而破坏平衡结果的操作有插入和删除。
三、节点的插入
红黑树新节点的插入有可能会破坏现有的平衡结构,所以就需要进行节点的变色、左旋或者右旋操作来保持红黑树的平衡;但插入操作的情况区分比较多,这也正是红黑树自平衡结构不容易理解的地方之一;
下图就是插入的所有情况,接下来会有针对每种情况进行详细讲解:
其他3种情况非常简单,这里详细说明第四种情况:
C - current 当前节点,P - parent 父节点,PP - 祖父节点,U - uncle 叔叔节点, O - 其他节点
情况4、P 为红色
4.1、P 为红色且 U 为红色 (如图 4.1)
(1)将 P 设为黑色
(2)将 U 设为黑色
(3)将 PP 设为红色
(4)将 PP 设为当前节点(这时 PP 就为 C 节点了),重复情况4判断