说完了二叉树的遍历、添加和删除引伸到了红黑树的遍历、添加和删除。对二叉树结构有了必定的了解,在这篇文章中将会对红黑树性质进行详细的说明。java
二叉树在理想状况下时间复杂度是O(logn),最坏状况下当插入的数据由小到大或者由大到小排列的时候,二叉树就变成了一个链表,而咱们知道链表检索的时间复杂度是O(n),效率很是差,因此出现了AVL树和红黑树来改变这种情况。同时因为AVL树的极端平衡特性,致使添加和删除数据后须要过多的旋转操做来保证AVL树平衡的特征,因此TreeMap中会使用红黑树来存储数据。 最好状况下的二叉树: spa
当AVL树在添加或者删除节点时出现不平衡后经过什么操做来保证树的平衡性呢?这种操做就叫作书旋转。红黑树也是如此,不过红黑树更复杂,这点咱们后面再说,先来看看AVL树的旋转操做。 树旋转操做是因为二叉树在添加节点时为了不出现平衡失效的状况而作的一种操做,操做的基本原则是操做后不影响二叉树中序遍历的结果。 这里咱们用AVL树来讲明这个问题。AVL树是一种高度平衡的二叉树,他的任何两个节点的子树的高度最大差异为1,这样他的查找、插入和删除的时间复杂度都是O(logn),当出现不平衡状况的时候,就须要执行树旋转。3d
树的旋转操做分为两种,左旋转和右旋转,这两种旋转是相对的。经过右旋或者左旋操做咱们可使一棵树继续保持平衡状态,并不会改变中序遍历的结果,但同时也要付出相应的代价。 code
//右旋
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;
}
}
//左旋
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;
}
}
复制代码
当树的任何两个节点的子树的高度差大于1的时候,就须要进行旋转以保证任何两个节点的子树的高度最大差异为1。哪几种状况下须要进行树旋转操做? 1.左左状况,左节点比右节点多两个节点,而且多出的节点都在左子树; cdn
明白了AVL树的旋转操做,再来看红黑树就简单多了,红黑树就是一颗知足必定条件的,相对平衡的二叉树,是二叉树和AVL树的一种折中。 红黑树的添加删除操做同二叉树同样,可是当添加删除等操做后使红黑树失去了他的特性后,就须要进行旋转操做来恢复红黑树的特性。 红黑树须要知足如下几点性质: 1.每一个节点要么是红色,要么是黑色。 2.根节点永远是黑色的。 3.全部的叶节点都是空节点(即 null),而且是黑色的。 4.每一个红色节点的两个子节点都是黑色。(从每一个叶子到根的路径上不会有两个连续的红色节点) 5.从任一节点到其子树中每一个叶子节点的路径都包含相同数量的黑色节点。 性质1和性质2很好理解。性质3在Java里面指的是空节点,通常不用考虑。性质4保证了从根到叶子节点的最长路径最多只能是最短路径的两倍,根据性质5创建一颗黑色节点为3的红黑树,最短路径为黑-黑-黑,最长路径为黑-红-黑-红-黑-红(由于每一个红色节点的两个子节点都是黑色,红色则不可连续)。 下图是一颗标准的红黑树: blog
在上一篇文章中的添加操做后调用了fixAfterInsertion(e)方法用来修复被破坏的红黑树性质。 通常默认每一个添加的节点都为红色,由于添加的节点若是为黑色,那就必定会破坏性质5,并且很难修复。但若是添加的是红色节点,有可能就不会破坏任何性质,也可能会破坏性质4致使连续的红色节点,能够经过变色和旋转来修复。 在添加红色节点时可能会遇到如下几种状况: 1.新节点为根节点,父节点为空,破坏性质2,修复红色为黑色便可。 it
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
//状况2 x.parent.color == BLACK
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
//状况3 父节点的兄弟节点也为红色 不须要旋转
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
//状况4 父节点的兄弟节点为黑色 父节点为祖父节点的左节点 x为父节点的右节点 先左旋变为状况5
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
//状况5 父节点的兄弟节点为黑色 父节点为祖父节点的左节点 x也为父节点的左节点 改变父节点为黑色 祖父节点为红色 而后祖父节点右旋
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
//状况3
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
//状况4 父节点的兄弟节点为黑色 父节点为祖父节点的右节点 x为父节点的左节点 先右旋变为状况5
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
//状况5 父节点的兄弟节点为黑色 父节点为祖父节点的右节点 x也为父节点的右节点 改变父节点为黑色 祖父节点为红色 而后祖父节点左旋
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
//状况1
root.color = BLACK;
}
复制代码
红黑树在删除后调用了fixAfterDeletion(p)用来修复被破坏的红黑树性质。 因为在删除时咱们采用后继节点替换的方法,替换以后只须要删除替换的节点便可。这样删除节点的问题就能够转换为删除至多只有1个孩子节点的问题(由于后继节点至多只有右孩子,或者没有孩子)。 删除时有如下几种状况: 1.删除的节点为红色,根据红色节点不可连续,则他的父节点和子节点都为黑色,直接用他的黑色子节点替换便可,删除了红色节点不会破坏性质5。 2.删除的节点为黑色,可是儿子为红色,直接用红色节点替换,替换后变红色节点为黑色便可。 3.删除的节点为黑色,同时儿子也为黑色,这种状况比较复杂,又能够分为几种状况: 将儿子命名为S,儿子替换后的父亲命名为F,儿子替换后的兄弟命名为B,兄弟的左节点命名为BL,兄弟的右节点命名为BR,状况3又能够分为如下几种状况: 4.F为任意色,B为红色,将F左旋转,并交换F和B的颜色,则经过各自路径的黑色节点数目不变,但S如今有了一个红色的父节点F,一个黑色的兄弟节点B,则状况能够变成五、6或者7。 io
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
Entry<K,V> sib = rightOf(parentOf(x));
//状况4 F为任意色,B为红色 状况能够变成五、6或者7
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
//状况5 F为任意色,B为黑色,BL和BR也为黑色
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
//状况6 F为任意色,B为黑色,BL为红色,BR为黑色
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
//状况7 F为任意色,B为黑色,BL为黑色,BR为红色
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric
Entry<K,V> sib = leftOf(parentOf(x));
//状况4 F为任意色,B为红色 状况能够变成五、6或者7
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
//状况5 F为任意色,B为黑色,BL和BR也为黑色
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
//状况6 F为任意色,B为黑色,BL为红色,BR为黑色 旋转着色后变为状况7
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
//状况7 F为任意色,B为黑色,BL为黑色,BR为红色
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
setColor(x, BLACK);
}
复制代码
理解了红黑树遍历,删除添加等操做的分析,再理解TreeMap<K,V>实现逻辑就会很容易。TreeMap<K,V>全部的操做都是在红黑树的基础上执行的,红黑树的每个节点对应为TreeMap<K,V>的一个Entry<K,V>。 TreeSet<E>因为在实现上彻底使用了TreeMap<K,V>的key来实现,因此TreeSet<E>的全部操做同样是创建在红黑树的基础上。function