一、关于红黑二叉树简介
TreeMap 是红黑二叉树算法实现。
- 红黑树简介
红黑树又称红-黑二叉树,它首先是一颗二叉树,它具有二叉树所有的特性。同时红黑树更是一颗自平衡的排序二叉树。
基本的二叉树需要满足一个基本性质–即树中的任何节点的值大于它的左子节点,且小于它的右子节点。按照这个基本性质使得树的检索效率大大提高。
我们知道在生成二叉树的过程是非常容易失衡的,最坏的情况就是一边倒(只有右/左子树),这样势必会导致二叉树的检索效率大大降低(O(n)),所以为了维持二叉树的平衡,大牛们提出了各种实现的算法,如:AVL,SBT,伸展树,TREAP ,红黑树等等。
平衡二叉树必须具备如下特性:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。也就是说该二叉树的任何一个等等子节点,其左右子树的高度都相近。
红黑树顾名思义就是红色或者黑色平衡二叉树,通过颜色约束二叉树的平衡。
对于一颗有效且平衡的二叉树需要遵守如下规则:
- 每个节点只能是红色或者黑色
- 根节点是黑色
- 每个叶节点(NIL 节点,空节点)是黑色
- 一个节点是红色,则它的两个节点都是黑色,不能包含两个相邻的红色节点
- 从任何一个节点到叶子节点,黑色节点的个数是相同的
详细了解红黑树结构,请参考:
1、红黑树数据结构剖析:http://www.cnblogs.com/fanzhidongyzby/p/3187912.html
2、红黑二叉树详解及理论分析 :http://blog.csdn.NET/kartorz/article/details/8865997
3、教你透彻了解红黑树 :blog.csdn.Net/v_july_v/article/details/6105630
4、经典算法研究系列:五、红黑树算法的实现与剖析 :http://blog.csdn.net/v_JULY_v/article/details/6109153
5、示例,红黑树插入和删除过程:http://saturnman.blog.163.com/blog/static/557611201097221570/
6、红黑二叉树详解及理论分析 :http://blog.csdn.net/kartorz/article/details/8865997
二、TreeMap重要方法源码解析
put 方法:
public V put(K key, V value) {
Entry<K,V> t = root;
//<1>获取根节点,如果根节点为null,作为根节点插入
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
//<2>获取排序仪
Comparator<? super K> cpr = comparator;
if (cpr != null) {
//<2.1> 排序仪器不为空,do-while循环遍历寻找节点最底层叶子节点
do {
parent = t;
cmp = cpr.compare(key, t.key);
//根据“即树中的任何节点的值大于它的左子节点,且小于它的右子节点”特性,生成如下逻辑,
//如果 源节点key小于目标节点key,从"左"节点遍历,否则从"右"节点遍历。
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
//<2.2> 排序仪器为空,按自然排序
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);
}
//<3>遍历到最底层节点位置,作为新节点的父节点
Entry<K,V> e = new Entry<>(key, value, parent);
//<3.1>还是遵守“即树中的任何节点的值大于它的左子节点,且小于它的右子节点”特性
//新key小于父节点key,作为左节点插入;大于,则作为右节点插入
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
删除节点
/**
* Delete node p, and then rebalance the tree.
*/
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
//<1>当前节点有左节点和右节点
if (p.left != null && p.right != null) {
//<1.1>寻找替换节点,注:删除不是直接删除节点,而是找替换节点替换当前节点
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children
// Start fixup at replacement node, if it exists.
//<2> 如果<1>找到的替换节点包含左节点,则用左节点替换,否则用右节点替换
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
//<2.1> 替换节点不为空
if (replacement != null) {
// Link replacement to parent
replacement.parent = p.parent;
//<2.1.1>当前要删除节点为根节点,直接将替换节点设置为根节点
if (p.parent == null)
root = replacement;
//<2.1.2>当前要删除节点为左节点,替换节点替换左节点
else if (p == p.parent.left)
p.parent.left = replacement;
//<2.1.3>当前要删除节点为右节点,替换节点替换右节点
else
p.parent.right = replacement;
//<2.2> 删除P节点
p.left = p.right = p.parent = null;
//<2.3> 如果当前节点颜色为黑色,调整红黑树使其保持平衡
//注:遵守红黑树特性:任意节点下的子节点的黑色节点树相同
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) { // <2.2>当前要删除节点为父节点.
root = null;
} else { //<2.3>当前要删除节点没有子节点,直接删除该节点
if (p.color == BLACK)
fixAfterDeletion(p);
if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
/**
* 选择替换节点
*/
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
if (t == null)
return null;
/*
* 寻找右子树的最左子树
*/
else if (t.right != null) {
Entry<K,V> p = t.right;
while (p.left != null)
p = p.left;
return p;
}
/*
* 选择左子树的最右子树
*/
else {
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
/**
* 替换节点后,调整红黑二叉树,使其保持平衡
*/
private void fixAfterDeletion(Entry<K,V> x) {
// 删除节点需要一直迭代,知道 直到 x 不是根节点,且 x 的颜色是黑色
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) { //若X节点为左节点
//获取其兄弟节点
Entry<K,V> sib = rightOf(parentOf(x));
/*
* 如果兄弟节点为红色----(情况3.1)
* 策略:改变W、P的颜色,然后进行一次左旋转
*/
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
/*
* 若兄弟节点的两个子节点都为黑色----(情况3.2)
* 策略:将兄弟节点编程红色
*/
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
}
else {
/*
* 如果兄弟节点只有右子树为黑色----(情况3.3)
* 策略:将兄弟节点与其左子树进行颜色互换然后进行右转
* 这时情况会转变为3.4
*/
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
/*
*----情况3.4
*策略:交换兄弟节点和父节点的颜色,
*同时将兄弟节点右子树设置为黑色,最后左旋转
*/
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
}
/**
* X节点为右节点与其为做节点处理过程差不多,这里就不在累述了
*/
else {
Entry<K,V> 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);
}