刪除元素本身比較簡(jiǎn)單,就是采用二叉樹的刪除規(guī)則。
創(chuàng)新互聯(lián)公司主要從事網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)龍文,十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):18982081108
(1)如果刪除的位置有兩個(gè)葉子節(jié)點(diǎn),則從其右子樹中取最小的元素放到刪除的位置,然后把刪除位置移到替代元素的位置,進(jìn)入下一步。
(2)如果刪除的位置只有一個(gè)葉子節(jié)點(diǎn)(有可能是經(jīng)過(guò)第一步轉(zhuǎn)換后的刪除位置),則把那個(gè)葉子節(jié)點(diǎn)作為替代元素,放到刪除的位置,然后把這個(gè)葉子節(jié)點(diǎn)刪除。
(3)如果刪除的位置沒(méi)有葉子節(jié)點(diǎn),則直接把這個(gè)刪除位置的元素刪除即可。
(4)針對(duì)紅黑樹,如果刪除位置是黑色節(jié)點(diǎn),還需要做再平衡。
(5)如果有替代元素,則以替代元素作為當(dāng)前節(jié)點(diǎn)進(jìn)入再平衡。
(6)如果沒(méi)有替代元素,則以刪除的位置的元素作為當(dāng)前節(jié)點(diǎn)進(jìn)入再平衡,平衡之后再刪除這個(gè)節(jié)點(diǎn)。
public V remove(Object key) {
// 獲取節(jié)點(diǎn)
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
// 刪除節(jié)點(diǎn)
deleteEntry(p);
// 返回刪除的value
return oldValue;
}
private void deleteEntry(Entry<K,V> p) {
// 修改次數(shù)加1
modCount++;
// 元素個(gè)數(shù)減1
size--;
if (p.left != null && p.right != null) {
// 如果當(dāng)前節(jié)點(diǎn)既有左子節(jié)點(diǎn),又有右子節(jié)點(diǎn)
// 取其右子樹中最小的節(jié)點(diǎn)
Entry<K,V> s = successor(p);
// 用右子樹中最小節(jié)點(diǎn)的值替換當(dāng)前節(jié)點(diǎn)的值
p.key = s.key;
p.value = s.value;
// 把右子樹中最小節(jié)點(diǎn)設(shè)為當(dāng)前節(jié)點(diǎn)
p = s;
// 這種情況實(shí)際上并沒(méi)有刪除p節(jié)點(diǎn),而是把p節(jié)點(diǎn)的值改了,實(shí)際刪除的是p的后繼節(jié)點(diǎn)
}
// 如果原來(lái)的當(dāng)前節(jié)點(diǎn)(p)有2個(gè)子節(jié)點(diǎn),則當(dāng)前節(jié)點(diǎn)已經(jīng)變成原來(lái)p的右子樹中的最小節(jié)點(diǎn)了,也就是說(shuō)其沒(méi)有左子節(jié)點(diǎn)了
// 到這一步,p肯定只有一個(gè)子節(jié)點(diǎn)了
// 如果當(dāng)前節(jié)點(diǎn)有子節(jié)點(diǎn),則用子節(jié)點(diǎn)替換當(dāng)前節(jié)點(diǎn)
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {
// 把替換節(jié)點(diǎn)直接放到當(dāng)前節(jié)點(diǎn)的位置上(相當(dāng)于刪除了p,并把替換節(jié)點(diǎn)移動(dòng)過(guò)來(lái)了)
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;
// 將p的各項(xiàng)屬性都設(shè)為空
p.left = p.right = p.parent = null;
// 如果p是黑節(jié)點(diǎn),則需要再平衡
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) {
// 如果當(dāng)前節(jié)點(diǎn)就是根節(jié)點(diǎn),則直接將根節(jié)點(diǎn)設(shè)為空即可
root = null;
} else {
// 如果當(dāng)前節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn)且其為黑節(jié)點(diǎn),則把自己當(dāng)作虛擬的替換節(jié)點(diǎn)進(jìn)行再平衡
if (p.color == BLACK)
fixAfterDeletion(p);
// 平衡完成后刪除當(dāng)前節(jié)點(diǎn)(與父節(jié)點(diǎn)斷絕關(guān)系)
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;
}
}
}
經(jīng)過(guò)上面的處理,真正刪除的肯定是黑色節(jié)點(diǎn)才會(huì)進(jìn)入到再平衡階段。
因?yàn)閯h除的是黑色節(jié)點(diǎn),導(dǎo)致整顆樹不平衡了,所以這里我們假設(shè)把刪除的黑色賦予當(dāng)前節(jié)點(diǎn),這樣當(dāng)前節(jié)點(diǎn)除了它自已的顏色還多了一個(gè)黑色,那么:
(1)如果當(dāng)前節(jié)點(diǎn)是根節(jié)點(diǎn),則直接涂黑即可,不需要再平衡;
(2)如果當(dāng)前節(jié)點(diǎn)是紅+黑節(jié)點(diǎn),則直接涂黑即可,不需要平衡;
(3)如果當(dāng)前節(jié)點(diǎn)是黑+黑節(jié)點(diǎn),則我們只要通過(guò)旋轉(zhuǎn)把這個(gè)多出來(lái)的黑色不斷的向上傳遞到一個(gè)紅色節(jié)點(diǎn)即可,這又可能會(huì)出現(xiàn)以下四種情況:
(假設(shè)當(dāng)前節(jié)點(diǎn)為父節(jié)點(diǎn)的左子節(jié)點(diǎn))
情況 | 策略 |
---|---|
1)x是黑+黑節(jié)點(diǎn),x的兄弟是紅節(jié)點(diǎn) | (1)將兄弟節(jié)點(diǎn)設(shè)為黑色;<br>(2)將父節(jié)點(diǎn)設(shè)為紅色;<br>(3)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行左旋;<br>(4)重新設(shè)置x的兄弟節(jié)點(diǎn),進(jìn)入下一步; |
2)x是黑+黑節(jié)點(diǎn),x的兄弟是黑節(jié)點(diǎn),且兄弟節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色 | (1)將兄弟節(jié)點(diǎn)設(shè)置為紅色;<br>(2)將x的父節(jié)點(diǎn)作為新的當(dāng)前節(jié)點(diǎn),進(jìn)入下一次循環(huán); |
3)x是黑+黑節(jié)點(diǎn),x的兄弟是黑節(jié)點(diǎn),且兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)為黑色,左子節(jié)點(diǎn)為紅色 | (1)將兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)設(shè)為黑色;<br>(2)將兄弟節(jié)點(diǎn)設(shè)為紅色;<br>(3)以兄弟節(jié)點(diǎn)為支點(diǎn)進(jìn)行右旋;<br>(4)重新設(shè)置x的兄弟節(jié)點(diǎn),進(jìn)入下一步; |
3)x是黑+黑節(jié)點(diǎn),x的兄弟是黑節(jié)點(diǎn),且兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)為紅色,左子節(jié)點(diǎn)任意顏色 | (1)將兄弟節(jié)點(diǎn)的顏色設(shè)為父節(jié)點(diǎn)的顏色;<br>(2)將父節(jié)點(diǎn)設(shè)為黑色;<br>(3)將兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)設(shè)為黑色;<br>(4)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行左旋;<br>(5)將root作為新的當(dāng)前節(jié)點(diǎn)(退出循環(huán)); |
(假設(shè)當(dāng)前節(jié)點(diǎn)為父節(jié)點(diǎn)的右子節(jié)點(diǎn),正好反過(guò)來(lái))
情況 | 策略 |
---|---|
1)x是黑+黑節(jié)點(diǎn),x的兄弟是紅節(jié)點(diǎn) | (1)將兄弟節(jié)點(diǎn)設(shè)為黑色;<br>(2)將父節(jié)點(diǎn)設(shè)為紅色;<br>(3)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行右旋;<br>(4)重新設(shè)置x的兄弟節(jié)點(diǎn),進(jìn)入下一步; |
2)x是黑+黑節(jié)點(diǎn),x的兄弟是黑節(jié)點(diǎn),且兄弟節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色 | (1)將兄弟節(jié)點(diǎn)設(shè)置為紅色;<br>(2)將x的父節(jié)點(diǎn)作為新的當(dāng)前節(jié)點(diǎn),進(jìn)入下一次循環(huán); |
3)x是黑+黑節(jié)點(diǎn),x的兄弟是黑節(jié)點(diǎn),且兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)為黑色,右子節(jié)點(diǎn)為紅色 | (1)將兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)設(shè)為黑色;<br>(2)將兄弟節(jié)點(diǎn)設(shè)為紅色;<br>(3)以兄弟節(jié)點(diǎn)為支點(diǎn)進(jìn)行左旋;<br>(4)重新設(shè)置x的兄弟節(jié)點(diǎn),進(jìn)入下一步; |
3)x是黑+黑節(jié)點(diǎn),x的兄弟是黑節(jié)點(diǎn),且兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)為紅色,右子節(jié)點(diǎn)任意顏色 | (1)將兄弟節(jié)點(diǎn)的顏色設(shè)為父節(jié)點(diǎn)的顏色;<br>(2)將父節(jié)點(diǎn)設(shè)為黑色;<br>(3)將兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)設(shè)為黑色;<br>(4)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行右旋;<br>(5)將root作為新的當(dāng)前節(jié)點(diǎn)(退出循環(huán)); |
讓我們來(lái)看看TreeMap中的實(shí)現(xiàn):
/**
* 刪除再平衡
*(1)每個(gè)節(jié)點(diǎn)或者是黑色,或者是紅色。
*(2)根節(jié)點(diǎn)是黑色。
*(3)每個(gè)葉子節(jié)點(diǎn)(NIL)是黑色。(注意:這里葉子節(jié)點(diǎn),是指為空(NIL或NULL)的葉子節(jié)點(diǎn)!)
*(4)如果一個(gè)節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的。
*(5)從一個(gè)節(jié)點(diǎn)到該節(jié)點(diǎn)的子孫節(jié)點(diǎn)的所有路徑上包含相同數(shù)目的黑節(jié)點(diǎn)。
*/
private void fixAfterDeletion(Entry<K,V> x) {
// 只有當(dāng)前節(jié)點(diǎn)不是根節(jié)點(diǎn)且當(dāng)前節(jié)點(diǎn)是黑色時(shí)才進(jìn)入循環(huán)
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
// 如果當(dāng)前節(jié)點(diǎn)是其父節(jié)點(diǎn)的左子節(jié)點(diǎn)
// sib是當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)
Entry<K,V> sib = rightOf(parentOf(x));
// 情況1)如果兄弟節(jié)點(diǎn)是紅色
if (colorOf(sib) == RED) {
// (1)將兄弟節(jié)點(diǎn)設(shè)為黑色
setColor(sib, BLACK);
// (2)將父節(jié)點(diǎn)設(shè)為紅色
setColor(parentOf(x), RED);
// (3)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行左旋
rotateLeft(parentOf(x));
// (4)重新設(shè)置x的兄弟節(jié)點(diǎn),進(jìn)入下一步
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
// 情況2)如果兄弟節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色
// (1)將兄弟節(jié)點(diǎn)設(shè)置為紅色
setColor(sib, RED);
// (2)將x的父節(jié)點(diǎn)作為新的當(dāng)前節(jié)點(diǎn),進(jìn)入下一次循環(huán)
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
// 情況3)如果兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)為黑色
// (1)將兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)設(shè)為黑色
setColor(leftOf(sib), BLACK);
// (2)將兄弟節(jié)點(diǎn)設(shè)為紅色
setColor(sib, RED);
// (3)以兄弟節(jié)點(diǎn)為支點(diǎn)進(jìn)行右旋
rotateRight(sib);
// (4)重新設(shè)置x的兄弟節(jié)點(diǎn)
sib = rightOf(parentOf(x));
}
// 情況4)
// (1)將兄弟節(jié)點(diǎn)的顏色設(shè)為父節(jié)點(diǎn)的顏色
setColor(sib, colorOf(parentOf(x)));
// (2)將父節(jié)點(diǎn)設(shè)為黑色
setColor(parentOf(x), BLACK);
// (3)將兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)設(shè)為黑色
setColor(rightOf(sib), BLACK);
// (4)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行左旋
rotateLeft(parentOf(x));
// (5)將root作為新的當(dāng)前節(jié)點(diǎn)(退出循環(huán))
x = root;
}
} else { // symmetric
// 如果當(dāng)前節(jié)點(diǎn)是其父節(jié)點(diǎn)的右子節(jié)點(diǎn)
// sib是當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)
Entry<K,V> sib = leftOf(parentOf(x));
// 情況1)如果兄弟節(jié)點(diǎn)是紅色
if (colorOf(sib) == RED) {
// (1)將兄弟節(jié)點(diǎn)設(shè)為黑色
setColor(sib, BLACK);
// (2)將父節(jié)點(diǎn)設(shè)為紅色
setColor(parentOf(x), RED);
// (3)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行右旋
rotateRight(parentOf(x));
// (4)重新設(shè)置x的兄弟節(jié)點(diǎn)
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
// 情況2)如果兄弟節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色
// (1)將兄弟節(jié)點(diǎn)設(shè)置為紅色
setColor(sib, RED);
// (2)將x的父節(jié)點(diǎn)作為新的當(dāng)前節(jié)點(diǎn),進(jìn)入下一次循環(huán)
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
// 情況3)如果兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)為黑色
// (1)將兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)設(shè)為黑色
setColor(rightOf(sib), BLACK);
// (2)將兄弟節(jié)點(diǎn)設(shè)為紅色
setColor(sib, RED);
// (3)以兄弟節(jié)點(diǎn)為支點(diǎn)進(jìn)行左旋
rotateLeft(sib);
// (4)重新設(shè)置x的兄弟節(jié)點(diǎn)
sib = leftOf(parentOf(x));
}
// 情況4)
// (1)將兄弟節(jié)點(diǎn)的顏色設(shè)為父節(jié)點(diǎn)的顏色
setColor(sib, colorOf(parentOf(x)));
// (2)將父節(jié)點(diǎn)設(shè)為黑色
setColor(parentOf(x), BLACK);
// (3)將兄弟節(jié)點(diǎn)的左子節(jié)點(diǎn)設(shè)為黑色
setColor(leftOf(sib), BLACK);
// (4)以父節(jié)點(diǎn)為支點(diǎn)進(jìn)行右旋
rotateRight(parentOf(x));
// (5)將root作為新的當(dāng)前節(jié)點(diǎn)(退出循環(huán))
x = root;
}
}
}
// 退出條件為多出來(lái)的黑色向上傳遞到了根節(jié)點(diǎn)或者紅節(jié)點(diǎn)
// 則將x設(shè)為黑色即可滿足紅黑樹規(guī)則
setColor(x, BLACK);
}
假設(shè)我們有下面這樣一顆紅黑樹。
我們刪除6號(hào)元素,則從右子樹中找到了最小元素7,7又沒(méi)有子節(jié)點(diǎn)了,所以把7作為當(dāng)前節(jié)點(diǎn)進(jìn)行再平衡。
我們看到7是黑節(jié)點(diǎn),且其兄弟為黑節(jié)點(diǎn),且其兄弟的兩個(gè)子節(jié)點(diǎn)都是紅色,滿足情況4),平衡之后如下圖所示。
我們?cè)賱h除7號(hào)元素,則從右子樹中找到了最小元素8,8有子節(jié)點(diǎn)且為黑色,所以8的子節(jié)點(diǎn)9是替代節(jié)點(diǎn),以9為當(dāng)前節(jié)點(diǎn)進(jìn)行再平衡。
我們發(fā)現(xiàn)9是紅節(jié)點(diǎn),則直接把它涂成黑色即滿足了紅黑樹的特性,不需要再過(guò)多的平衡了。
這次我們來(lái)個(gè)狠的,把根節(jié)點(diǎn)刪除,從右子樹中找到了最小的元素5,5沒(méi)有子節(jié)點(diǎn),所以把5作為當(dāng)前節(jié)點(diǎn)進(jìn)行再平衡。
我們看到5是黑節(jié)點(diǎn),且其兄弟為紅色,符合情況1),平衡之后如下圖所示,然后進(jìn)入情況2)。
對(duì)情況2)進(jìn)行再平衡后如下圖所示。
然后進(jìn)入下一次循環(huán),發(fā)現(xiàn)不符合循環(huán)條件了,直接把x涂為黑色即可,退出這個(gè)方法之后會(huì)把舊x刪除掉(見(jiàn)deleteEntry()方法),最后的結(jié)果就是下面這樣。
未完待續(xù),下一節(jié)我們一起探討紅黑樹遍歷元素的操作。
當(dāng)前標(biāo)題:死磕java集合之TreeMap源碼分析(三)-內(nèi)含紅黑樹分析全過(guò)程
轉(zhuǎn)載來(lái)于:http://muchs.cn/article2/pieeoc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)、服務(wù)器托管、營(yíng)銷型網(wǎng)站建設(shè)、軟件開發(fā)、網(wǎng)站維護(hù)、面包屑導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)