概念:
紅黑樹或者是一顆空樹,或者是一顆具有以下性質的二叉查找樹.
- 結點非紅即黑.
- 根結點為黑.
- 所有的NULL為葉子結點,且認為顏色為黑
- 所有紅結點的子結點為黑
- 任一結點到其葉子結點的所有路徑,經過的黑結點數量相同
-
任意一個結點,他的左右子樹層次不超過一倍.
image.png
結點插入:
先按照二叉排序樹的方式插入一個節點(紅色).
插入的是黑結點>直接將結點涂黑.
插入結點的父節點是黑色>不違背任何性質,不用調整.
-
插入結點的父結點是紅色>
4.1 父結點是祖父結點的左孩子
4.1.1 祖父節點的另一個子結點(叔叔結點)是紅色>
將當前節點的父結點和叔叔結點涂黑,祖父結點涂紅,將當前節點指定為祖父結點,從新的當前節點開始算法.
image.png
4.1.2 叔叔結點是黑色>
4.1.2.1 當前節點是其父結點的右孩子>當前節點的父結點作為新的當前節點,以新的當前節點為支點進行左旋
image.png
4.1.2.2 當前節點是其父結點的左孩子>父結點變為黑色,祖父結點變為紅色,再以祖父結點為支點進行右旋
image.png4.2 父結點是祖父結點右孩子>和上面一樣,只是把左全部換成右.
刪除結點
先進行二叉排序樹的刪除操作,然后將替換結點作為新的當前節點進行后面的平衡操作.
當前節點是紅色>直接把當前節點染黑,結束.
-
當前節點是黑色>
2.1 被刪除節點是父結點的左孩子>
2.1.1 當前節點是根結點>什么都不做
2.1.2 當前節點X的兄弟結點是紅色(此時,父結點和兄弟節點的子結點為黑)>把父結點染紅,兄弟節點染黑,以父結點為支點進行左旋,重新設置X的兄弟節點.
image.png
2.1.3 當前節點X的兄弟節點是黑色>
2.1.3.1 兄弟節點的兩個孩子都為黑色>將當前節點的兄弟節點染黑,父結點染紅,然后以父結點作為新的當前節點,從新開始算法.
image.png
2.1.3.2 兄弟的右孩子是黑色,左孩子是紅色>將X兄弟節點的左孩子置黑,將X節點的兄弟節點置紅,將X節點的兄弟節點進行右旋,然后重置x節點的兄弟節點.
image.png
2.1.3.3 兄弟節點的右孩子是紅色>兄弟節點染成當前節點父結點的顏色,兄弟節點的右孩子染成黑色,父結點染成黑色,以當前節點的父結點為支點,進行左旋,算法結束.
image.png2.2 被刪除節點是父結點的右孩子>把上邊的左置為右
代碼
紅黑樹的插入刪除規則就講完了,是有點復雜,但其實不難.
那么,我們來看看紅黑樹的代碼吧,jdk1.8之后,為了適用大數據時代,很多數據結構都用到了紅黑樹
比如:HashTable,TreeSet,TreeMap
我們以TreeMap為例,來看看紅黑樹的源碼,對照上面的插入規則和刪除規則來一步步看
- TreeMap插入操作
public V put(K key, V value) {
TreeMapEntry<K,V> t = root;
if (t == null) {//根結點為空
// BEGIN Android-changed: Work around buggy comparators. http://b/34084348
// We could just call compare(key, key) for its side effect of checking the type and
// nullness of the input key. However, several applications seem to have written comparators
// that only expect to be called on elements that aren't equal to each other (after
// making assumptions about the domain of the map). Clearly, such comparators are bogus
// because get() would never work, but TreeSets are frequently used for sorting a set
// of distinct elements.
//
// As a temporary work around, we perform the null & instanceof checks by hand so that
// we can guarantee that elements are never compared against themselves.
//
// **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
//
// Upstream code was:
// compare(key, key); // type (and possibly null) check
if (comparator != null) {//這個comparator表示傳進來的比較器
if (key == null) {
comparator.compare(key, key);
}
} else {
if (key == null) {
throw new NullPointerException("key == null");
} else if (!(key instanceof Comparable)) {
throw new ClassCastException(
"Cannot cast" + key.getClass().getName() + " to Comparable.");
}
}
// END Android-changed: Work around buggy comparators. http://b/34084348
root = new TreeMapEntry<>(key, value, null);//插入結點作為根結點
size = 1;
modCount++;//表示TreeMap被修改一次
return null;
}
int cmp;
TreeMapEntry<K,V> parent;
// split comparator and comparable paths
//這里是確認插入結點的位置,分為comparator為空和不為空兩種情況
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();
@SuppressWarnings("unchecked")
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);
}
//插入節點,平衡操作
TreeMapEntry<K,V> e = new TreeMapEntry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
/** From CLR */
private void fixAfterInsertion(TreeMapEntry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {//4
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//4.1
TreeMapEntry<K,V> y = rightOf(parentOf(parentOf(x)));//x的叔叔結點y
if (colorOf(y) == RED) {//4.1.1
setColor(parentOf(x), BLACK);//父結點置黑
setColor(y, BLACK);//叔叔結點置黑
setColor(parentOf(parentOf(x)), RED);//祖父結點置紅
x = parentOf(parentOf(x));//祖父結點作為當前節點
} else {//4.1.2
if (x == rightOf(parentOf(x))) {//4.1.2.1
x = parentOf(x);
rotateLeft(x);
}
//4.1.2.2
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {//4.2
TreeMapEntry<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;//2
}
- TreeMap刪除操作
/**
* Delete node p, and then rebalance the tree.
*/
private void deleteEntry(TreeMapEntry<K,V> p) {
modCount++;
size--;
// If strictly internal, copy successor's element to p and then make p
// point to successor.
if (p.left != null && p.right != null) {
TreeMapEntry<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.
TreeMapEntry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {
// Link replacement to parent
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
// Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null;
// Fix replacement
if (p.color == BLACK)//2
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
root = null;
} else { // No children. Use self as phantom replacement and unlink.
if (p.color == BLACK)//2
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;
}
}
}
/** From CLR */
private void fixAfterDeletion(TreeMapEntry<K,V> x) {
while (x != root && colorOf(x) == BLACK) {//2
if (x == leftOf(parentOf(x))) {//2.1
TreeMapEntry<K,V> sib = rightOf(parentOf(x));//sib,當前節點的兄弟結點
if (colorOf(sib) == RED) {//2.1.2
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {//2.1.3.1
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {//2.1.3.2
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
//2.1.3.3
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric
//2.2
TreeMapEntry<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);//1
}
/**
* Returns the successor of the specified Entry, or null if no such.
*/
static <K,V> TreeMapEntry<K,V> successor(TreeMapEntry<K,V> t) {
if (t == null)
return null;
else if (t.right != null) {//右子樹不為空時,得到刪除結點右子樹的左子樹中最小的結點,替換被刪除的結點
TreeMapEntry<K,V> p = t.right;
while (p.left != null)
p = p.left;
return p;
} else {//右子樹為空,從當前節點一直右上找,找到頭
TreeMapEntry<K,V> p = t.parent;
TreeMapEntry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
ok,紅黑樹就介紹到這里了,如果對這些樹感興趣呢,其實在計算科學中還有很多有趣的樹
比如:
大家感興趣可以自行去學習下.
日拱一卒,貴在堅持.