package arraylist;
import java.util.Map;
/**
* @author mawt
* @description
* @date 2020/7/28
*/
public class TreeMap<K, V> {
private static final boolean RED = false;
private static final boolean BLACK = true;
private transient Entry<K, V> root;
static final class Entry<K, V> implements Map.Entry<K, V> {
K key;
V value;
Entry<K, V> left;
Entry<K, V> right;
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;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
return valEquals(key, e.getKey()) && valEquals(value, e.getValue());
}
public int hashCode() {
int keyHash = (key == null ? 0 : key.hashCode());
int valueHash = (value == null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}
}
/**
* 红黑树定义和性质
* 红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质:
* <p>
* 性质1:每个节点要么是黑色,要么是红色。
* 性质2:根节点是黑色。
* 性质3:每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
* 性质4:如果一个节点是红色的,则它的子节点必须是黑色的。
* 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
* <p>
* 从性质5又可以推出:
* 性质5.1:如果一个结点A存在黑子结点,那么该结点A肯定有两个子结点
* <p>
* 节点情况汇总:
* 1.新增节点颜色:红、黑
* 2.新增节点位置:左、右
* 3.父节点颜色:红、黑
* 4.父节点位置:左、右
* 5.叔叔节点:不存在、存在且为红色、存在且为黑色
* 总计有 2 * 2 * 2 * 2 * 3 = 48种情况需要讨论
* <p>
* 前提:
* 1.插入节点x前,该树一定满足红黑树的所有特性;
* 2.插入节点x后经过旋转或变色,该树一定满足红黑树的所有特性
*
* I.情况1:新增节点颜色一定为红(如果为黑就破坏了红黑树平衡5此时就需要额外操作来平衡红黑树了)
* 此时就只有2 * 2 * 2 * 3 = 24种情况需要讨论了
*
* II.情况3:如果父节点为黑色,此时新增节点为红,并没有破坏红黑树平衡,自平衡就此结束了,所有我们只需要讨论父节点为红色的情况就可以了
* 此时就只有2 * 2 * 3 = 12种情况需要讨论了
* 需要讨论的有: 2.新增节点位置:左、右
* 4.父节点位置:左、右
* 5.叔叔节点:不存在、存在且为红色、存在且为黑色
*
* III.经过II的分析,此时父节点为红色,祖父节点一定存在并且是黑色(红黑树特性4)
* 先讨论5:假设叔叔节点r存在并且为黑色,情况就会是这样:
* 黑pp 黑pp
* 红p 黑r 黑r 红p
* 因为在新插入节点前,该树一定要满足红黑树所有特性,但是这2种情况并不满足,所有这2种情况并不存在,就不需要讨论了
* 此时就只有2 * 2 * 2 = 8种情况需要讨论了
* 需要讨论的有: 2.新增节点位置:左、右
* 4.父节点位置:左、右
* 5.叔叔节点:不存在、存在且为红色
*
* IV.
* 树1: 黑pp
* 红p
* 红x
*
* 树2: 黑pp
* 红p
* 红x
*
* 树3: 黑pp
* 红p
* 红x
*
* 树4: 黑pp
* 红p
* 红x
*
* 树5: 黑pp
* 红p 红r
* 红x
*
* 树6: 黑pp
* 红p 红r
* 红x
*
* 树7: 黑pp
* 红r 红p
* 红x
*
* 树8: 黑pp
* 红r 红p
* 红x
*
*
* @param x
*/
private void fixAfterInsertion(Entry<K, V> x) {
x.color = RED; //默认新增节点颜色为red
while (x != null && x != root && x.parent.color == RED) { //父节点为红色,祖父节点一定是黑色
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //父节点是左子树
Entry<K, V> y = rightOf(parentOf(parentOf(x))); // 叔叔节点
if (colorOf(y) == RED) { //叔叔节点存在并且是红色 例5和例6
setColor(parentOf(x), BLACK); // 黑pp 黑pp
setColor(y, BLACK); // 红p 红y 红p 红y
setColor(parentOf(parentOf(x)), RED);// 红x 红x
x = parentOf(parentOf(x));
//存在连续的2个红节点,违反了红黑树特性,需要平衡
//1.如果将红x变黑,那么红p的左子树凭空多了一个黑色节点,违反了红黑树特性5,
// 所以我们必须将红p的右子树添加一个黑节点,不好搞,放弃此种变色
//2.如果将红p变黑,那么黑pp的左子树凭空多了一个黑色节点,违反了红黑树特性5,
// 所以直接将红y变黑,那么从黑pp出发满足红黑树特性5,如果黑pp有父节点ppp,
// 那么ppp的左子树到任意叶子结点的路径中多了一个黑色节点,为了满足特性5,
// 我们将黑pp变红,满足红黑树特性5,但是此时为什么不退出while循环呢,
// 因为我们将黑pp变红后,并不清楚ppp是哪种颜色,有可能不满足红黑树特性4,
// 此时有可能是这样:
// 红ppp
// 红pp
// 黑p 黑y
// 红x
// 所有执行x = parentOf(parentOf(x))将x执行红pp继续while循环,
// 如果将x执行黑p或者黑y时,它们作为新插入节点时颜色并不是红色,跟新插入节点颜色为red矛盾
} else { //叔叔节点不存在或叔叔节点为黑色
if (x == rightOf(parentOf(x))) { // 黑pp 黑pp
//当前节点是右子节点 例2 // 红p 红p 黑y 此种情况不可能发生
x = parentOf(x); // 红x 红x
rotateLeft(x);//左旋p
//存在连续的2个红节点,违反了红黑树特性,需要平衡
//1.如果将红x变黑,那么红p的左子树凭空多了一个黑色节点,违反了红黑树特性5,
// 所以我们必须将红p的右子树添加一个黑节点,不好搞,放弃此种变色
//2.如果将红p变黑,那么黑pp的左子树凭空多了一个黑色节点,
// 黑pp的右子树为null,违反了红黑树特性5,放弃此种变色
//3.因为红x在红p的右子树上,需要对红p进行左旋,变成这样了:
// 黑pp
// 红xx 红xx就是原来的红x
// 红x 红x就是原来的红p
} else { //例1
//当前节点是左子节点 // 黑pp 黑pp
// 红p 红p 黑y 此种情况不可能发生
// 红x 红x
}
//经过ifelse后,变成这样了
// 黑pp
// 红p
// 红x
//1.将红p变黑,黑pp变红,右旋黑pp,变成这样了:
// 黑p
// 红x 红pp
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) { //叔叔节点存在并且是红色 例7和例8
setColor(parentOf(x), BLACK); // 黑pp 黑pp
setColor(y, BLACK); // 红y 红p 红y 红p
setColor(parentOf(parentOf(x)), RED);// 红x 红x
x = parentOf(parentOf(x));
} else { //叔叔节点不存在或叔叔节点为黑色
if (x == leftOf(parentOf(x))) { // 黑pp 黑pp
//当前节点是左子节点 例4 // 红p 黑y 红p 此种情况不可能发生
x = parentOf(x); // 红x 红x
rotateRight(x);
} else { //例3
//当前节点是右子节点 // 黑pp 黑pp
// 红p 黑y 红p 此种情况不可能发生
// 红x 红x
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}
static final boolean valEquals(Object o1, Object o2) {
return (o1 == null ? o2 == null : o1.equals(o2));
}
private static <K, V> boolean colorOf(Entry<K, V> p) {
return (p == null ? BLACK : p.color);
}
private static <K, V> Entry<K, V> parentOf(Entry<K, V> p) {
return (p == null ? null : p.parent);
}
private static <K, V> void setColor(Entry<K, V> p, boolean c) {
if (p != null)
p.color = c;
}
private static <K, V> Entry<K, V> leftOf(Entry<K, V> p) {
return (p == null) ? null : p.left;
}
private static <K, V> Entry<K, V> rightOf(Entry<K, V> p) {
return (p == null) ? null : p.right;
}
private void rotateLeft(Entry<K, V> p) {
if (p != null) {
Entry<K, V> r = p.right;
p.right = r.left;
if (r.left != null)
r.left.parent = p;
r.parent = p.parent;
if (p.parent == null)
root = r;
else if (p.parent.left == p)
p.parent.left = r;
else
p.parent.right = r;
r.left = p;
p.parent = r;
}
}
private void rotateRight(Entry<K, V> p) {
if (p != null) {
Entry<K, V> l = p.left;
p.left = l.right;
if (l.right != null) l.right.parent = p;
l.parent = p.parent;
if (p.parent == null)
root = l;
else if (p.parent.right == p)
p.parent.right = l;
else p.parent.left = l;
l.right = p;
p.parent = l;
}
}
}
基于jdk1.8 TreeMap详解红黑树插入原理
猜你喜欢
转载自blog.csdn.net/qq_33436466/article/details/107655041
今日推荐
周排行