图解红黑树的插入和删除过程
红黑树是一种弱平衡的二叉查找树,其查找过程与普通的二叉树一样(都是二分查找),其特别之处在于插入或删除一个结点时修复平衡的策略。而jdk8的java.uitl.TreeMap.java的底层是红黑树,在走读了TreeMap的代码和参考了如下的资料后,本文用图表把红黑树的插入和删除过程直观第表示出来。
参考资料:
从2-3-4树到红黑树:https://www.cnblogs.com/nullzx/p/6111175.html
证明黑色平衡:https://www.cnblogs.com/skywang12345/p/3245399.html
定义
(1) 节点是要么红色或要么是黑色。
(2) 根一定是黑色节点。
(3) 每个叶子结点都带有两个空的黑色结点(称之为NIL节点,它又被称为黑哨兵)。
(4) 红色不相连:每个红色节点的两个子节点都是黑色。
(5) 黑色平衡:从任一节点到它所能到达得叶子节点的所有简单路径都包含相同数目的黑色节点。
推论:
(4) (5) ==> 红色结点的两个子结点,要么同时为黑色非空结点,要么同时为空结点
(5) ==> 黑色结点的两个子结点,不可能出现一黑一空的情况,否则违反规则(5)
性质
性质1:结点数为 n n n 的红黑树,其高度至多为 2 log ( n + 1 ) 2\log(n+1) 2log(n+1) 。故查找复杂度为 o ( log n ) o(\log n) o(logn)
证明:https://www.cnblogs.com/skywang12345/p/3245399.html
用一个图来简略地验证:红黑树的最坏情况是它所对应的2-3-4树中构成最左边的路径结点全部都是3-结点,而其余均为2-结点,如下图,共14个结点,高度为
2
log
(
14
+
1
)
=
7
2\log(14+1)=7
2log(14+1)=7 。
性质2:红黑树的本质是2-3-4树,将红色结点与其父结点(父结点必然是黑色结点)合并可以变成一个3-结点或4-结点。
插入
向红黑树中插入一个新的结点,要保持红黑树定义中的两个约束:①红色不相连;②黑色平衡
插入策略:
由于红色结点不影响黑色平衡,每次插入新结点时,新结点的颜色都设为红色。基于这种插入策略,可能会导致红色结点与红色结点相连,所以,插入完之后的修复策略是处理红色结点与红色结点相连的情况。
插入后的修复策略:
(1)若新插入结点为根结点或新插入结点的父结点为黑色结点,则无须处理。
(2)若新插入结点的父结点为红色结点,则将新插入的结点设为"当前结点"。
六种场景及其处理方法,如下表所示:
修复过程,本质上是对调爷、父结点的颜色先使当前结点恢复“红不相连”。若叔结点为黑色,则叔分支的黑高少了1,此时通过旋转爷结点恢复叔分支的黑高,并使得父结点继承了爷结点的位置和颜色(黑色);若叔结点为红色,则因为爷结点已变为了红色所以叔结点要变为黑色,以保持叔分支的“红不相连”和黑高,由于爷结点变为了红色,可能违反“红不相连”,故将爷结点设置为当前结点,继续修复的过程。
父结点是左子树和右子树是对称的场景,所以实际上只有两种不同的场景要处理,即①父红叔黑和②父红叔红。下图以父结点是左子树为例,用图来表达插入后修复的处理过程。
删除
删除一个结点,如果被删结点是红色的,那么红黑树的结构依然保持(黑色平衡、红不相连);但如果被删结点是黑色的,那么经过该结点的路径的黑高就减少了1,黑色平衡被破坏,这时就需要进行结构修复。
删除策略:
在红黑树中,任意删除一个结点,最终会变为删除树中倒数第一、二层的一个结点。
证明:这是因为被删结点有三种类型,①该结点是叶子结点,②该结点其左右子树只有一个不为空,③该结点左右子树都不为空。对于①,叶子显然属于倒数第1层。对于②,若其中一个子树为空而另一个不为空,那么不为空的子树只能是一个红色的结点,否则不满足黑平衡和红不连,所以该结点在倒数第2层。对于③,用该结点的后继结点替代它的值,然后取删除这个后继结点,而这个后继结点的左子树必然为空,即后继结点是①或②类型的结点。
删除后的修复策略:
(1)若被删结点是红色结点或被删结点是唯一的一个结点(即变成了空树),则无须处理。
(2)若被删结点是黑色结点,则将继承结点设置为“当前结点”,然后修复的本质是使得经过当前结点的路径的黑高+1,而当前结点的兄结点的黑高不变。
下图以当前结点是黑色左子树为例,用图来表达修复的处理过程。