上文详细讲解了红黑树的概念,红黑树的插入及旋转操作,根据测试代码建立起来的红黑树结构为:
本文先研究一下红黑树的移除操作是如何实现的,移除操作比较复杂,具体移除的操作要进行几次旋转和移除的节点在红黑树中的位置有关,这里也不特意按照旋转次数选择节点了,就找三种位置举例演示红黑树移除操作如何进行:
第2行的代码是获取待移除的节点Entry,做法很简单,拿key与当前节点按指定算法做一个比较获取cmp,cmp=0表示当前节点就是待移除的节点;cmp0,取右子节点继续比较;cmp0,取左子节点继续比较clashx移除节点。。
根据上面的流程图,根节点30左右子节点不为空,因此要先选择继承者,选择继承者的流程为:
由于节点30的右子节点不为空,因此从节点70开始不断取左子节点直到取到叶子节点为止,最终取到的节点s为节点50
p=s,即p原来指向的是root节点,现在p指向s节点,p与s指向同一份内存空间,即节点50
接着选择replacement,由于p与s指向同一份内存空间,因此replacement判断的是s是否有左子节点,显然s没有clash的节点会过期吗,因此replacement为空
replacement为空,但是p有父节点,因此可以判断出来p也就是节点50不是root节点
最后节点p是其父节点的左子节点,因此节点p的左子节点置为null,再将p的父节点置为null,相当于把节点p移除
接着看一下移除中间节点TreeMap是怎么做的,这里以移除节点70为例,继续分点整理一下移除节点70做了什么:
节点70有左右子节点,因此还是选择继承者,由于节点70的右子节点85没有左子节点文件怎么导入clash,因此选出来的继承者就是节点85
replacement为null且节点p即节点85有父节点,根据流程图可知,节点p是一个黑色节点,因此需要进行删除数据修正
最后节点p是其父节点的右子节点,因此节点p的右子节点置为null,再将p的父节点置为null,相当于把节点p移除
总体流程和移除根节点差不多,唯一的区别是节点85是一个黑色节点,因此需要进行一次删除数据修正操作。删除数据修正实现为fixAfterDeletion方法,它的源码:
方法第3行~第30行与第30行~第57行是对称的,因此只分析一下前半部分也就是第3行~第30行的代码。第三行的代码”x == leftOf(parentOf(x))”很显然判断的是x是否其父节点的左子节点。其流程图为:
p指向右下角的黑色节点85,对此节点进行修正,上面的流程图是p是父节点的左子节点的流程,这里的p是父节点的右子节点,没太大区别。
sib取父节点的左子节点即节点60,节点60是一个黑色节点,因此这里不需要进行一次旋转。
接着,sib的左右子节点不是黑色节点且sib的左子节点为红色节点,因此这里只需要进行一次旋转的流程:
最后一步的操作在fixAfterDeletion方法的外层,节点85的父节点不为空,因此将节点85的父节点置空clashx移除节点,最终移除节点70之后的数据结构为:
最后看看移除最底下节点的场景clashx移除节点。,以移除节点85为例,节点85根据代码以节点p称呼。
节点p没有左右子节点,因此节点p不需要进行选择继承者的操作;同样的由于节点p没有左右子节点,因此选择出来的replacement为null。
接着由于replacement为null但是节点p是一个黑色节点,黑色节点需要进行删除修正流程:
sib的左子节点是红色的,因此这里需要进行的操作是将sib着色为p父节点的颜色红色、将p的父节点着色为黑色、将sib的左子节点着色为黑色、将p的父节点进行一次右旋
最后还是一样,回到fixAfterDeletion方法外层的代码,将p的父节点置为null,即节点p就不在当前数据结构中了,完成移除,红黑树最终的结构为: