我们知道,TreeMap是一个基于key有序的二叉查找树,其底层实现的原理是一颗红黑树。本文从源码的角度,分析TreeMap的最重要的几个方法,来对红黑树的原理作出讲解。
1.所有节点不是红色就是黑色
2.红色节点后面必须是黑色节点
3.根节点必须是黑色节点
4.叶子节点必须是黑色节点
5.任意一个节点到叶子节点的黑色节点必须相同
private void rotateLeft(Entry p) {
if (p != null) { //中心节点不为空才进行左旋,p为上图的X节点
Entry r = p.right; // 将Y节点赋给r
p.right = r.left; //将β赋给X的左子树
if (r.left != null)//如果β不为空
r.left.parent = p; //将X设为β的父
r.parent = p.parent; //将X的父亲赋给Y
if (p.parent == null) //如果X的父亲为空
root = r; //将Y设置为新的根节点
else if (p.parent.left == p) //如果X是其父的左子树
p.parent.left = r; //将其父的左子树设置为Y
else
p.parent.right = r; //否则,将其父的又子树设置为Y
r.left = p; // 将X设置为Y的左子树
p.parent = r; //将X的父设置为Y
}
}
private void rotateRight(Entry p) {
if (p != null) { //如果Y节点不为空节点,进行右旋转
Entry l = p.left; //用l引用X
p.left = l.right; //将β设置为Y的左子树
if (l.right != null)
l.right.parent = p; //如果β不为空,将β的父设置为Y
l.parent = p.parent; //将X的父赋给Y的父
if (p.parent == null) //如果Y的父为空
root = l; //将X设为树的根
else if (p.parent.right == p) //如果Y是父的的右子树
p.parent.right = l; //将Y的父的右子树设置为X
else p.parent.left = l; //负责Y的父的左子树设置为X
l.right = p; //X的右子树设置为Y
p.parent = l; //Y的父设置为X
}
}
public V put(K key, V value) {
Entry t = root; //
if (t == null) {
compare(key, key); // 无意义的比较,防止空指针异常
root = new Entry<>(key, value, null); //搞一个Entry出来
size = 1; //树的大小为1
modCount++; //树被修改的次数设置为1
return null; //返回空,树中无此条目,所以也无旧值
}
int cmp; //比较当前节点和key的大小的临时变量
Entry parent; //当前节点的父应用
// split comparator and comparable paths
Comparator super K> cpr = comparator;
if (cpr != null) { //如果比较器不为空,用比较器比较
do {
parent = t;
cmp = cpr.compare(key, t.key); //当前节点和key比较
if (cmp < 0) //小于0,走左子树
t = t.left;
else if (cmp > 0) //大于0,走右子树
t = t.right;
else //如果等于0,说明找到目标条目,新值替换旧值,旧值返回
return t.setValue(value);
} while (t != null); //一直比较直到t为空,说明没找到目标节点,说明要进行插入操作
}
else { //如果没有比较器,说明自身可比较,大致流程和上面相同
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable super K> k = (Comparable super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry e = new Entry<>(key, value, parent); //生成一个新的结点,父就是最后跟踪到的非叶子节点
if (cmp < 0) //如果小于0,说明最后一次比较,走到父的左孩子上去了
parent.left = e; //将父的左孩子设置为e
else
parent.right = e; //将父的右孩子设置为e
fixAfterInsertion(e); //修复树,因为新增接待你会破坏红黑树的特性(重点)
size++; //大小加1
modCount++; //修改次数加1
return null; //新增节点,没有旧值,返回null
}
//红黑树算法的重点,下面不要害怕,会图文分解讲解
private void fixAfterInsertion(Entry x) {
x.color = RED; //所有新增加的节点,着红色,因为红色不会为这条路径增加黑色节点,破坏特性5
// 通过变色和旋转,使得红黑树重新复合5条特性,从新增加的节点开始调整
// 当x为根,x为空或者x的父为黑,停止调整
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //x的父为x爷爷的左子树
Entry y = rightOf(parentOf(parentOf(x))); //y为x的叔叔
//如果y也为红色,说明他爸和他叔都为红色,此时他爷爷肯定为黑色
//此时让他爸爸叔叔和他爷爷换一下颜色,即可局部搞定
// 情况1
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK); //让他爸变黑
setColor(y, BLACK); //让他叔叔变黑
setColor(parentOf(parentOf(x)), RED); //让他爷爷变红
//将爷爷作为当前节点,继续调整
//因为他爷爷现在是红色,而他爷爷的爸也可能是红色,所以要继续调整
x = parentOf(parentOf(x));
} else {
//此时,他爸为红,他叔叔为黑色,不好搞,只能通过旋转
// 如果我为我爸的右子树,此时需要我爸左旋,等待下一步处理
//旋转后,我爸变成了我,我变成了我爸,原来的爸成为当前处理节点
if (x == rightOf(parentOf(x))) { //情况2
x = parentOf(x); //情况2
rotateLeft(x); //情况2
}
setColor(parentOf(x), BLACK); //爸爸变成黑的 情况3
setColor(parentOf(parentOf(x)), RED); //将爷爷变为红的 情况3
rotateRight(parentOf(parentOf(x))); // 右旋搞定 情况3
}
} else {
// 他爸是爷爷的右边节点,处理情况类似上面
Entry y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK; //将根变为黑色,防止根被调整为红色退出调整
}
节点的删除肯定不是叶子节点,当删除的节点本身是黑色需要进行调整,当删除的节点是红色时,无需对红黑树进行调整。
private void deleteEntry(Entry p) {
modCount++; //节点的修改次数加1
size--; //树的节点减1
//当左子树右子树都不为空时,需要找到P的后继节点来与P交换
//同时删除P的后继节点
//p的后继节点应该是P最右边的子孙
if (p.left != null && p.right != null) {
Entry s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
}
//找到要删除的节点,此时该节点要么左子树为空,要么又子树为空,或者全空
//找到不为空的子树来顶替P的位置
Entry replacement = (p.left != null ? p.left : p.right);
//当替代者不为空时,要用替代者顶替P
if (replacement != null) {
// p的父赋值给替代者的父
replacement.parent = p.parent;
if (p.parent == null)
root = replacement; //如果P的父为空,那么将替代者设为根节点
else if (p == p.parent.left)
p.parent.left = replacement; //P为父的左,将替代者设置为父的左
else
p.parent.right = replacement; //P为父的右,将替代者设置为父的右
// Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null; //释放P
// 如果被删的P是黑色,那么需要修复树,
// 目标是来弥补p的缺失让通往叶子节点路径的黑色节点数量少一的损失
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) {
// 如果替代者为空,说明无左右孩子,且为根,说明树就这么一个节点
root = null; //将树设为空
} else { // 无孩子不为根
if (p.color == BLACK) //自身是黑色,也需要通过修复补偿黑节点丢失的损失
fixAfterDeletion(p);
if (p.parent != null) { //自身为红,将父母的指向设为空就ok
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null; //自身对父母的指针也设为空
}
}
}
/** From CLR */
private void fixAfterDeletion(Entry x) {
//调整结束的标准是x为根节点或者x本身为红色
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) { //如果x是父的左孩子,以此为例解释,右边对称处理
Entry sib = rightOf(parentOf(x)); //sib是兄弟节点
if (colorOf(sib) == RED) { // 如果兄弟节点是红色,下图情况1
setColor(sib, BLACK); // 将兄弟节点变黑
setColor(parentOf(x), RED); //将父节点变红
rotateLeft(parentOf(x)); //以父节点为中心左旋
sib = rightOf(parentOf(x)); //从新获取新的兄弟节点
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) { //如果左右侄子都为黑,情况2
setColor(sib, RED); //将兄弟设置为红
x = parentOf(x); //将x的父变成新的x
} else {
if (colorOf(rightOf(sib)) == BLACK) { //如果左侄子为红,右侄子为黑,情况3
setColor(leftOf(sib), BLACK); // 左侄子变黑
setColor(sib, RED); //兄弟变红
rotateRight(sib); //以兄弟为核心,右旋
sib = rightOf(parentOf(x)); //左侄子变为兄弟
}
//此时左侄子为黑,又侄子为红,情况4
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;
}
}
}
//将x设置为黑色,防止调整到最后,根为红色,情况1可能导致这种情况
setColor(x, BLACK);
}