在上篇中围绕源码讲了TreeMap的一些方法,本文主要阐述其如何维持特性:
①.节点是红色或黑色
②.根节点是黑色
③.每一个叶节点(NIL节点,空节点)是黑色的
④.每一个红色节点的两个子节点都是黑色。(从每一个叶子到根的全部路径上不能有两个连续的红色节点)
⑤.从任一节点到其每一个叶子的全部路径都包含相同数目的黑色节点
注意:
性质1和性质3老是保持着
性质4只在增长红色节点、重绘黑色节点为红色,或作旋转时受到威胁
性质5只在增长黑色节点、重绘红色节点为黑色,或作旋转时受到威胁
post
private void fixAfterInsertion(Entry x) {
// 新插入的节点默认红色
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
// 若x的父节点是其父节点的父节点的左子树
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//获取x父节点的父节点的右子树y
Entry y = rightOf(parentOf(parentOf(x)));
//若y颜色为红色
if (colorOf(y) == RED) {
//x父节点的颜色设为黑色
setColor(parentOf(x), BLACK);
//y的颜色设为黑色
setColor(y, BLACK);
//x父节点的父节点颜色设为红色
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
//若x为其父节点的右子树
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
//左旋转
rotateLeft(x);
}
//x父节点的颜色设为黑色
setColor(parentOf(x), BLACK);
//x父节点的父节点颜色设为红色
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
//获取x父节点的父节点的左子树y
Entry y = leftOf(parentOf(parentOf(x)));
//若y颜色为红色
if (colorOf(y) == RED) {
//x父节点设为黑色
setColor(parentOf(x), BLACK);
//y颜色设为黑色
setColor(y, BLACK);
//x父节点的父节点颜色设为红色
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
//若y颜色为黑色
} else {
//若x为父节点的左子树
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
//右旋转
rotateRight(x);
}
//x父节点颜色设为黑色
setColor(parentOf(x), BLACK);
//x父节点的父节点颜色设为红色
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
//根节点必须为黑色
root.color = BLACK;
}
复制代码
新增有三点:
①.新增结点颜色为红色(方法中第一行)
②.若新增结点的父节点颜色是黑色,能够维持性质
③.如有任何红黑性质被破坏,则至多只有一条被破坏,或是性质2,或是性质4。若性质2被破坏,其缘由为新增结点是根节点且颜色为红,若性质4被破坏,其缘由为新增结点与其父节点颜色都为红色spa
此情形不会调用到fixAfterInsertion方法,在put方法中直接经过Entry类构造方法建立根节点,建立出的结点默认黑色,符合红黑树性质无需平衡。
put方法部分代码:
3d
public V put(K key, V value) {
Entry t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
//键,值,父节点
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
.
.
.
}
复制代码
此状况也无需平衡不会走进fixAfterInsertion方法循环体内,由于新增节点老是红色,又由于性质4其会有两个黑色NIL节点,因此经过它的每一个子节点的路径依然包含相同数目的黑色节点知足性质5。
实例如图添加节点12:
code
如图添加节点19,咱们能够发现其违反了性质4(每一个红色节点的两个子节点都是黑色)。 cdn
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//获取x的叔父节点
Entry y = rightOf(parentOf(parentOf(x)));
//若y颜色为红色
if (colorOf(y) == RED) {
//x父节点的颜色设为黑色
setColor(parentOf(x), BLACK);
//y的颜色设为黑色
setColor(y, BLACK);
//x父节点的父节点颜色设为红色
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
}
}
复制代码
先将父节点与叔父节点颜色改成黑色,祖父结点颜色改成红色,结果如图:
blog
实例如图添加节点15:
get
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
.
.
.
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
}
}
/**
* 左旋转
*/
private void rotateLeft(Entry p) {
if (p != null) {
//获取p的右子树r
Entry r = p.right;
//将r的左子树设为p的右子树
p.right = r.left;
//若r的左子树不为空,则将p设为r左子树的父节点
if (r.left != null)
r.left.parent = p;
//将p的父节点设为r的父节点
r.parent = p.parent;
//若p的父节点为空,则r设为根节点
if (p.parent == null)
root = r;
//若p为其父节点左子树,则将r设为p父节点左子树
else if (p.parent.left == p)
p.parent.left = r;
//不然r设为p的父节点的右子树
else
p.parent.right = r;
将p设为r的左子树
r.left = p;
将r设为p的父节点
p.parent = r;
}
}
复制代码
左旋转动态图以下: 源码
/**
* 右旋转
*/
private void rotateRight(Entry p) {
if (p != null) {
//获取p的左子树l
Entry l = p.left;
将l的右子树设为p的左子树
p.left = l.right;
//若l的右子树不为空,则将p设为l的右子树的父节点
if (l.right != null)
l.right.parent = p;
//将p的父节点设为l的父节点
l.parent = p.parent;
//若p的父节点为空,则将l设为根节点
if (p.parent == null)
root = l;
//若p为p父节点的右子树,则将l设置为p的父节点的右子树
else if (p.parent.right == p)
p.parent.right = l;
//不然将l设为p的父节点的左子树
else p.parent.left = l;
//将p设为l的右子树
l.right = p;
//将l设为p的父节点
p.parent = l;
}
}
复制代码
右旋转动态图以下: it
场景五:新增节点父节点是红色,而叔父节点是黑色或缺乏,新节点是其父节点的左子树,而父节点又是其父节点的左子树
实例如图添加节点14:
io
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
.
.
.
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
}
复制代码
操做后如图,符合红黑树性质:
删除平衡源码:
private void fixAfterDeletion(Entry x) {
while (x != root && colorOf(x) == BLACK) {
//若x为其父节点的左子树
if (x == leftOf(parentOf(x))) {
//获取x兄弟节点
Entry sib = rightOf(parentOf(x));
//若兄弟节点颜色为红色
if (colorOf(sib) == RED) {
//将兄弟节点改成黑色
setColor(sib, BLACK);
//将父节点改成红色
setColor(parentOf(x), RED);
//以父节点左旋转
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
//若兄弟节点的左子树与右子树都为黑色
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
//若只有兄弟节点的右子树颜色为黑色
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
//若x为其父节点的右子树
} else { // symmetric
Entry sib = leftOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
setColor(x, BLACK);
}
复制代码
在上篇文章所提到删除节点的状况:
①.叶子结点:直接将其父节点对应孩子置空,若删除左叶子结点则将其父结点左子树置空,若删除右叶子结点则将其父结点右子树置空
②.一个孩子:用子节点替代需删除节点
③.两个孩子:用后继节点替换待删除节点,而后删除后继节点
如上图删除节点1,结合代码,将节点1兄弟节点11改红色,再最后将其父节点8改红色:
//sib兄弟节点(这里只节点11)
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
//将兄弟节点改成红色
setColor(sib, RED);
//删除的节点(即节点1)指向父节点(节点8)
x = parentOf(x);
}
}
.
.
.
setColor(x, BLACK);
复制代码
如图删除节点1:
Entry sib = rightOf(parentOf(x));
.
.
.
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
.
.
.
setColor(x, BLACK);
复制代码
此场景走此源码分支代码,兄弟节点左子树改(节点9)为黑色,兄弟节点(节点11)改成红色,以兄弟节点右旋转,再将其指向旋转后的兄弟节点,if分支代码走完,操做后如图:
再将旋转后的兄弟节点(节点9)颜色改成父节点(节点8)颜色(红色),将父节点改成黑色,将兄弟节点右子树(节点11)改成黑色,以父节点左转,最后x指向root退出循环且运行最后一行确保根节点黑色
此状况就是不走上述if分支其他同样再也不过多写了
如图删除节点11:
if (x == leftOf(parentOf(x))) {
Entry sib = rightOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
}
setColor(x, BLACK);
复制代码
将兄弟节点(节点15)改成黑色,父节点(节点13)改成红色,以父节点左旋转,旋转后:
注意这里只列了兄弟节点红色且其两个孩子都为黑色或无孩子,其余状况能够转换成2.2,2.3
此场景用子节点替代需删除节点,平衡较为简单。
如上图删除节点17(红色),只需将根节点右子树设为节点15,红黑树性质依旧保持无需平衡
删除节点8(黑色),将节点11左子树设为节点1,由于节点1,11都为红色违反性质4(每一个红色节点的两个子节点都是黑色),进行调色平衡节点1改成黑色便可,删除后
此状况能够转换成场景一中1处理
能够转换成场景一中2.1处理
能够转换成场景一中2.2处理
能够转换成场景一中2.3处理
能够转换成场景一中2.4处理
本文列举TreeMap插入删除平衡操做结合源码阐述其原理,如有错误望指正。。。
维基百科——红黑树