1.前言
这个方法的作用是:当红黑树中新插入一个节点时,需要对树的结构重新优化,以保证该树始终保持红黑树的特性。
红黑树的特性:
- 每个结点是黑色或者红色。
- 根结点是黑色。
- 每个叶子结点(NIL)是黑色。 [注意:这里叶子结点,是指为空(NIL或NULL)的叶子结点!]
- 如果一个结点是红色的,则它的子结点必须是黑色的。
- 每个结点到叶子结点NIL所经过的黑色结点的个数一样的。[确保没有一条路径会比其他路径长出俩倍,所以红黑树是相对接近平衡的二叉树的!]
2.源码
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
//1. x:插入的节点,root:红黑树的根节点,先把x的颜色设为红色
x.red = true;
// 这里定义了一个循环,没有结束条件,即只能从内部退出
// xp:x节点的父节点,xpp:x节点的父父节点,xppl:x节点的父父节点的左子节点,xppr:x节点的父父节点的右子节点
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
//2. 这里判断 把x的父节点先赋给 xp,当xp为null 时,则说明x就是root节点,无需进行平衡
if ((xp = x.parent) == null) {
// 把x的颜色设为黑色
x.red = false;
// 直接返回 x
return x;
}
//3. 当 xp的颜色是黑色的,或者 【xp的颜色是红色且它的parent是null,这种情况不会出现,这样写通常是为了赋值】
else if (!xp.red || (xpp = xp.parent) == null)
return root;
// 4. 如果当前的父节点节点 == 当前节点的父父节点的左子节点
if (xp == (xppl = xpp.left)) {
// 4.1 把xpp.right赋给 xppr 当 xppr不为null且 xppr是红色的
if ((xppr = xpp.right) != null && xppr.red) {
// 把xppr修改为黑色
xppr.red = false;
// xp 修改为黑色的
xp.red = false;
// xpp修改为红色
xpp.red = true;
// 把 xpp 赋给 x
x = xpp;
}
else {
// 4.2.1 当 x== x父节点的右子节点时,即x被放到了右边
if (x == xp.right) {
// 父节点左旋 2.1 rotateLeft 详解
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
// 4.2.2 当xp 不为 null时,把xp的颜色置为黑色
if (xp != null) {
xp.red = false;
// 4.2.2.1 当 xpp 不为null时,把 xpp的颜色置为红色
if (xpp != null) {
xpp.red = true;
// xpp 节点右旋, 2.2 rotateRight 详解
root = rotateRight(root, xpp);
}
}
}
}
// 5.
else {
// 5.1 当 xppl 不为null 且 xppl的颜色是红色时
if (xppl != null && xppl.red) {
// 把 xppl的颜色置为黑色
xppl.red = false;
// xp的颜色置为黑色
xp.red = false;
// xpp 的颜色设为红色
xpp.red = true;
// 把 xpp赋给 x
x = xpp;
}
// 5.2
else {
// 5.2.1 当x== xp的左子节点时,即x在它父节点的左边
if (x == xp.left) {
// 父节点右旋 2.2 rotateRight 详解
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
// 5.2.2 当xp不为null 时
if (xp != null) {
// 把xp的颜色设为 黑色
xp.red = false;
// 如果 xpp 不为null
if (xpp != null) {
// 把xpp的颜色设为红色
xpp.red = true;
// xpp 左旋 2.1 rotateLeft 详解
root = rotateLeft(root, xpp);
}
}
}
}
}
}
- 当新插入一个节点时,红黑树的某些特性会被破坏,这是就会调用这个方法使它达到自平衡的效果。主要有三种方式:
- 修改颜色
- 节点左旋
- 节点右旋 这里给大家贴一篇讲的很好的文章,我就不细讲了
2.1 rotateLeft()
// 节点左旋
// root 根节点,p要左旋的节点
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p) {
// r:p的右子节点,pp:p的的父节点 rl :r的左子节点
TreeNode<K,V> r, pp, rl;
// 当 p不等于 null && p.right(此时已把p.right赋给r) 不为null
// 即需要左旋时,p和p的右子节点不能为null
if (p != null && (r = p.right) != null) {
// 这里实际上是 先把 r.left赋给 p.right 然后再把 p.right 赋给 rl 即 r的左子节点
// 当 rl 不为null
if ((rl = p.right = r.left) != null)
// 把rl的parent 设为 p;
rl.parent = p;
// 把p.parent 赋给 r.parent 最后赋给 pp
// 当 p的父节点不为null时,即r就是root节点
if ((pp = r.parent = p.parent) == null)
// 把r赋给 root 把它的颜色设为 黑色
(root = r).red = false;
// 当 pp.left == p 即说明p是它父节点的左子节点
else if (pp.left == p)
// 这里把它父节点的左子节点 改为 r
pp.left = r;
else
// 否则的话 说明p是它父节点的右子节点
pp.right = r;
// 把r.的左子节点设为 p
r.left = p;
// p的父节点设为 r;
p.parent = r;
}
return root;
}
2.2 rotateRight()
// 节点右旋
// root 根节点,p要右旋的节点
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
TreeNode<K,V> p) {
// l:p的左子节点, pp:p的父节点,lr:l的右子节点
TreeNode<K,V> l, pp, lr;
// 当p不为null且P的左子节点不能为null(节点右旋的必要条件)
if (p != null && (l = p.left) != null) {
// l的右子节点 不为null时
if ((lr = p.left = l.right) != null)
// 因为上面已经把l的右子节点赋给了 p的左子节点,所以这里在把 lr的父节点设为 p
lr.parent = p;
// 判断当 pp 为null时
if ((pp = l.parent = p.parent) == null)
// 把l赋给root 并把它的颜色设为黑色
(root = l).red = false;
// 当 pp 的右子节点 就是p时,直接把pp的右子节点设为l
else if (pp.right == p)
pp.right = l;
// 否则把 pp的左子节点赋给 l
else
pp.left = l;
// l的右子节点设为p
l.right = p;
// p的父节点设为 l
p.parent = l;
}
//返回 root节点
return root;
}