在上篇中圍繞源碼講了TreeMap的一些方法,本文主要闡述其如何維持特性:
①.節點是紅色或黑色
②.根節點是黑色
③.每一個葉節點(NIL節點,空節點)是黑色的
④.每一個紅色節點的兩個子節點都是黑色。(從每一個葉子到根的全部路徑上不能有兩個連續的紅色節點)
⑤.從任一節點到其每一個葉子的全部路徑都包含相同數目的黑色節點
注意:
性質1和性質3老是保持着
性質4只在增長紅色節點、重繪黑色節點爲紅色,或作旋轉時受到威脅
性質5只在增長黑色節點、重繪紅色節點爲黑色,或作旋轉時受到威脅
post
private void fixAfterInsertion(Entry x) {
// 新插入的節點默認紅色
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
// 若x的父節點是其父節點的父節點的左子樹
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//獲取x父節點的父節點的右子樹y
Entry y = rightOf(parentOf(parentOf(x)));
//若y顏色爲紅色
if (colorOf(y) == RED) {
//x父節點的顏色設爲黑色
setColor(parentOf(x), BLACK);
//y的顏色設爲黑色
setColor(y, BLACK);
//x父節點的父節點顏色設爲紅色
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
//若x爲其父節點的右子樹
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
//左旋轉
rotateLeft(x);
}
//x父節點的顏色設爲黑色
setColor(parentOf(x), BLACK);
//x父節點的父節點顏色設爲紅色
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
//獲取x父節點的父節點的左子樹y
Entry y = leftOf(parentOf(parentOf(x)));
//若y顏色爲紅色
if (colorOf(y) == RED) {
//x父節點設爲黑色
setColor(parentOf(x), BLACK);
//y顏色設爲黑色
setColor(y, BLACK);
//x父節點的父節點顏色設爲紅色
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
//若y顏色爲黑色
} else {
//若x爲父節點的左子樹
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
//右旋轉
rotateRight(x);
}
//x父節點顏色設爲黑色
setColor(parentOf(x), BLACK);
//x父節點的父節點顏色設爲紅色
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
//根節點必須爲黑色
root.color = BLACK;
}
複製代碼
新增有三點:
①.新增結點顏色爲紅色(方法中第一行)
②.若新增結點的父節點顏色是黑色,能夠維持性質
③.如有任何紅黑性質被破壞,則至多隻有一條被破壞,或是性質2,或是性質4。若性質2被破壞,其緣由爲新增結點是根節點且顏色爲紅,若性質4被破壞,其緣由爲新增結點與其父節點顏色都爲紅色spa
此情形不會調用到fixAfterInsertion方法,在put方法中直接經過Entry類構造方法建立根節點,建立出的結點默認黑色,符合紅黑樹性質無需平衡。
put方法部分代碼:
3d
public V put(K key, V value) {
Entry t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
//鍵,值,父節點
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
.
.
.
}
複製代碼
此狀況也無需平衡不會走進fixAfterInsertion方法循環體內,由於新增節點老是紅色,又由於性質4其會有兩個黑色NIL節點,因此經過它的每一個子節點的路徑依然包含相同數目的黑色節點知足性質5。
實例如圖添加節點12:
code
如圖添加節點19,咱們能夠發現其違反了性質4(每一個紅色節點的兩個子節點都是黑色)。 cdn
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//獲取x的叔父節點
Entry y = rightOf(parentOf(parentOf(x)));
//若y顏色爲紅色
if (colorOf(y) == RED) {
//x父節點的顏色設爲黑色
setColor(parentOf(x), BLACK);
//y的顏色設爲黑色
setColor(y, BLACK);
//x父節點的父節點顏色設爲紅色
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
}
}
複製代碼
先將父節點與叔父節點顏色改成黑色,祖父結點顏色改成紅色,結果如圖:
blog
實例如圖添加節點15:
get
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
.
.
.
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
}
}
/**
* 左旋轉
*/
private void rotateLeft(Entry p) {
if (p != null) {
//獲取p的右子樹r
Entry r = p.right;
//將r的左子樹設爲p的右子樹
p.right = r.left;
//若r的左子樹不爲空,則將p設爲r左子樹的父節點
if (r.left != null)
r.left.parent = p;
//將p的父節點設爲r的父節點
r.parent = p.parent;
//若p的父節點爲空,則r設爲根節點
if (p.parent == null)
root = r;
//若p爲其父節點左子樹,則將r設爲p父節點左子樹
else if (p.parent.left == p)
p.parent.left = r;
//不然r設爲p的父節點的右子樹
else
p.parent.right = r;
將p設爲r的左子樹
r.left = p;
將r設爲p的父節點
p.parent = r;
}
}
複製代碼
左旋轉動態圖以下: 源碼
/**
* 右旋轉
*/
private void rotateRight(Entry p) {
if (p != null) {
//獲取p的左子樹l
Entry l = p.left;
將l的右子樹設爲p的左子樹
p.left = l.right;
//若l的右子樹不爲空,則將p設爲l的右子樹的父節點
if (l.right != null)
l.right.parent = p;
//將p的父節點設爲l的父節點
l.parent = p.parent;
//若p的父節點爲空,則將l設爲根節點
if (p.parent == null)
root = l;
//若p爲p父節點的右子樹,則將l設置爲p的父節點的右子樹
else if (p.parent.right == p)
p.parent.right = l;
//不然將l設爲p的父節點的左子樹
else p.parent.left = l;
//將p設爲l的右子樹
l.right = p;
//將l設爲p的父節點
p.parent = l;
}
}
複製代碼
右旋轉動態圖以下: it
場景五:新增節點父節點是紅色,而叔父節點是黑色或缺乏,新節點是其父節點的左子樹,而父節點又是其父節點的左子樹
實例如圖添加節點14:
io
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
.
.
.
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
}
複製代碼
操做後如圖,符合紅黑樹性質:
刪除平衡源碼:
private void fixAfterDeletion(Entry x) {
while (x != root && colorOf(x) == BLACK) {
//若x爲其父節點的左子樹
if (x == leftOf(parentOf(x))) {
//獲取x兄弟節點
Entry sib = rightOf(parentOf(x));
//若兄弟節點顏色爲紅色
if (colorOf(sib) == RED) {
//將兄弟節點改成黑色
setColor(sib, BLACK);
//將父節點改成紅色
setColor(parentOf(x), RED);
//以父節點左旋轉
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
//若兄弟節點的左子樹與右子樹都爲黑色
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
//若只有兄弟節點的右子樹顏色爲黑色
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
//若x爲其父節點的右子樹
} else { // symmetric
Entry 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,結合代碼,將節點1兄弟節點11改紅色,再最後將其父節點8改紅色:
//sib兄弟節點(這裏只節點11)
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
//將兄弟節點改成紅色
setColor(sib, RED);
//刪除的節點(即節點1)指向父節點(節點8)
x = parentOf(x);
}
}
.
.
.
setColor(x, BLACK);
複製代碼
如圖刪除節點1:
Entry sib = rightOf(parentOf(x));
.
.
.
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
.
.
.
setColor(x, BLACK);
複製代碼
此場景走此源碼分支代碼,兄弟節點左子樹改(節點9)爲黑色,兄弟節點(節點11)改成紅色,以兄弟節點右旋轉,再將其指向旋轉後的兄弟節點,if分支代碼走完,操做後如圖:
再將旋轉後的兄弟節點(節點9)顏色改成父節點(節點8)顏色(紅色),將父節點改成黑色,將兄弟節點右子樹(節點11)改成黑色,以父節點左轉,最後x指向root退出循環且運行最後一行確保根節點黑色
此狀況就是不走上述if分支其他同樣再也不過多寫了
如圖刪除節點11:
if (x == leftOf(parentOf(x))) {
Entry sib = rightOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
}
setColor(x, BLACK);
複製代碼
將兄弟節點(節點15)改成黑色,父節點(節點13)改成紅色,以父節點左旋轉,旋轉後:
注意這裏只列了兄弟節點紅色且其兩個孩子都爲黑色或無孩子,其餘狀況能夠轉換成2.2,2.3
此場景用子節點替代需刪除節點,平衡較爲簡單。
如上圖刪除節點17(紅色),只需將根節點右子樹設爲節點15,紅黑樹性質依舊保持無需平衡
刪除節點8(黑色),將節點11左子樹設爲節點1,由於節點1,11都爲紅色違反性質4(每一個紅色節點的兩個子節點都是黑色),進行調色平衡節點1改成黑色便可,刪除後
此狀況能夠轉換成場景一中1處理
能夠轉換成場景一中2.1處理
能夠轉換成場景一中2.2處理
能夠轉換成場景一中2.3處理
能夠轉換成場景一中2.4處理
本文列舉TreeMap插入刪除平衡操做結合源碼闡述其原理,如有錯誤望指正。。。
維基百科——紅黑樹