红黑树怎么来?历史缘由
先说二叉树,父节点劈叉出两个节点,就两个,不可以多。这样一层一层下去。
再说二叉查找树(基于二分查找),同样有上面的劈叉属性,但是呢左中右有大小顺序,左边小于中间,中间小于右边。
接着就是红黑树,干嘛上面下来就红黑,二叉树如果不加以控制就成单边树了。因为二叉树没规定不可以单边开叉,而且一直往下开叉,5左单边4再左单边3,再左单边2,类似的就失去了二叉查找树的意义了。这下红黑就出来了。
红黑为了达到楼上的特点就列出了五个特点
一、节点必须红黑两种颜色。treemap就用了boolean值分别表示
二、根节点必须黑色
三、没个尾节点下面是黑色节点(感觉没啥意义)
四、红色节点下面或者上面不可以连续两个出现
五、任意节点到他子节点经过的黑色节点个数一样多。
那这玩意搞着五个条件无非就是限制插入,删除。有人破坏树的情况下,限制的。
例如,插入一个节点,首先二分查找节点,再fix。fix就为了满足这五个规则。一般都是不符合后面这两条。红色不可以连着,任意节点到子节点黑色同样个数。
jdk经典案例,treeset、treemap。上酒,小二。
就treemap源码分析。
一、treemap内部类子节点除了map中的key和value,还有左右节点和父节点,最后特有颜色(boolean值),遍历都是一样的。
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left = null;
Entry<K,V> right = null;
Entry<K,V> parent;
boolean color = BLACK;
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
二、put查找出要插入的位置,也就是父节点。 fixAfterInsertion(e);这里是重点。
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
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<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
三、重点 fixAfterInsertion(e);
private void fixAfterInsertion(Entry<K,V> x) {
//直接红色,就要搞破坏,黑色就没啥了
x.color = RED;
//新增节点非空,且不是根节点,且新增节点(自己,以下称呼自己)父亲是红色,如果父亲是黑色也不搞事。
while (x != null && x != root && x.parent.color == RED) {
//父亲跟父亲的父亲的左节点equal?也就是父亲是否为祖父的左节点
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//祖父右节点,叔叔
Entry<K,V> y = rightOf(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 == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
//变换父亲(黑色)和祖父颜色(红色)
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
//右转祖父
rotateRight(parentOf(parentOf(x)));
}
} else {
Entry<K,V> 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;
}
不强求记,毕竟像红黑树,外面大把的实现。
可以画图,进一步分析为啥,等有空再搞