红黑树听起来挺吓人,但当你看完这篇文章后再加上红黑树在线生成练习几次,就能够轻松拿下。
下面的教程中x
代表当前插入的节点,xp
,xpp
,xppl
,xppr
,uncle
,nephewF(far)
,nephewC(close)
分别为父节点,祖父节点,祖父节点的左孩子,祖父节点的右孩子,叔叔节点,远侄子节点,近侄子节点。
左旋:逆时针旋转
右旋:顺时针旋转
reBalanceAfterInsert():我们在插入后会调用此方法进行再平衡
reBalanceAfterDelete():我们在删除节点后会调用此方法进行再平衡
1,根节点必须为黑色。
2,红色节点不能有红色节点的孩子
3,从根节点出发到达叶节点所经过的黑色节点个数相同
4,节点必须为红色或黑色
5,一般插入节点为红色
class RBTreeNode{
boolean red;
int val;
RBTreeNode left;
RBTreeNode right;
RBTreeNode parent;
}
红黑树本身就是一种二叉查找树,所以可以进行二分查找。这与AVL树或者BST是一致的。
插入时有几种基本情况,更多复杂的情况都可以转换成基本情况求解(重点)。左右都是镜像对称的。这里采用右边插入为例子。
当前树为空,则要插入的节点就是根节点,因此直接插入并将颜色置为黑。
要插入的节点的父节点为黑色,此时直接插入节点。因为增加一个红色节点不会影响当前插入路径上黑色节点个数变化。
当要插入的节点存在祖父节点,且不存在叔叔节点时,判断x
和xp
的位置关系,再判断xp
和xpp
的位置关系。如果是三个节点中一条线上时,称为三点一线
。此时我们要做的就是以xp
为圆心,xpp
进行左旋。在左旋完成后将xp
与xpp
颜色交换(暴力认为xp
置为黑色,xpp
置为红色)
if(xp.right == x){
// 再判断xpp
if(xpp.right == xp){
// 此时是图中的情况,这里称为三点一线
}
}
这个情况和基本情况3是类似的。只是结构上不再是三点一线
而是一种之字形
处理方式是,将x
和xp
根据位置情况进行旋转,然后转换成情况3。当x
是xp
的左孩子时,以x
为圆心xp
进行右旋。然后形成三点一线
状态,此时xp
位置就是x
了,再以x为圆心,xpp
进行左旋,变色即可。
当uncle
存在且为红色时,直接将uncle
和xp
变为黑色,xpp
变为红色,同时再调用reBalanceAfterInsert()
判断对剩余树颜色的影响。从图中看到,在xpp
变为红色后会进行reBalanceAfterInsert()
将根节点xpp
染成黑色。
当uncle
存在且为黑色时,只需要以xp
为圆心xpp
进行旋转并将uncle
置为红色即可。图中是先进行了情况5的调整,然后再进行情况6的调整。
对于删除来说需要考虑的比插入多一点情况,但是也不难。
首先看下整颗红黑树形态
删除的节点为黑色,且无左右子节点。比如删除50
。此时需要判断nephewF
也就是远侄子节点是否存在,对于上图50
的nephewF
节点为160
。此时远侄子节点存在且为红色,那么需要以兄弟节点 150
为圆心,xp 100
绕其进行左旋。然后将100
和160
染成黑色。
远侄子不存在,但是近侄子存在且为黑色。这里处理方式和插入时情况4
转成情况3
是一样的。以近侄子为圆心,兄弟节点绕其旋转,变成远侄子的情况,再使用情况2进行处理。
当出现当前节点无孩子,且存在兄弟节点也无孩子,且父节点为黑色时。
此时假如要删除150
,在删除后,左子树路径黑色节点个数少一个,因此需要将右边路径也-1,那么最快的方法就是将160
设置为红色,那么此时对于更大一层的树而言,155
节点那条路上也少了一个黑色节点,则需要再一次调用reBalanceAfterDelete()
。可以明显的看出以155
为调整的节点来说,存在远侄子节点450
,那么就转换成情况2。则接下来会以350
为圆心,175
绕其进行左旋,然后将250
置为175
的右孩子,350
变成根节点,然后450
被染成黑色。调整结束。
当存在兄弟节点并且父节点为红色时。如图
此时假设删除200
,那么删除后左边路径黑色节点个数少一个,应该将右边节点个数也-1,那么就将300
置为红色,然后250
置为黑色。调整结束。可以发现在经过上面方法调整后,并没有对更大层面产生影响。
当节点只有一个孩子且为红色时,直接将xp
指向child
,同时child
颜色变成黑色。以删除155
为例。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200322200355860.png
当存在左右孩子时,找到左子树中最大的节点C
,将其值赋给x
,然后转换成对C
的删除操作。
这里以删除350
为例,首先找到300
,然后将300
的值赋给350
节点,然后变成对300
这个红色节点的删除。
可能这个例子太简单了,这边还有一个例子供大家学习。
首先找到左子树最大节点250
,然后将250
的值赋给300
,再变成对250
的删除,此时发现存在近侄子情况,因此是情况3。将以近侄子160
为圆心,155
绕其进行一次左旋,随后变成情况2,再将175
以160
为圆心进行右旋,调整结束。