TreeMap之元素刪除
微信公眾號:I am CR7
如有問題或建議,請在下方留言
最近更新:2018-09-07
TreeMap之插入
通過上一篇文章,介紹了二分查詢樹的缺陷,引出了紅黑樹的介紹。進一步分析TreeMap中插入元素的原始碼,最後藉助示例來加深對於紅黑樹的理解。詳細請看TreeMap之元素插入
TreeMap之刪除
1、刪除指定key對應的元素:
1public V remove(Object key) { 2//根據key獲取鍵值對 3Entry<K,V> p = getEntry(key); 4if (p == null) 5return null; 6//刪除該鍵值對,並返回value值 7V oldValue = p.value; 8deleteEntry(p); 9return oldValue; 10} 複製程式碼
2、刪除節點,並對樹進行平衡調整:
2.1、原始碼:
1private void deleteEntry(Entry<K,V> p) { 2modCount++; 3size--; 4 5//如果存在左右節點,則獲取其後繼節點,完成key、value的複製,p指向後繼節點 6//此處思想就是將有左右節點的節點p的刪除轉化為其後繼節點的刪除 7if (p.left != null && p.right != null) { 8Entry<K,V> s = successor(p); 9p.key = s.key; 10p.value = s.value; 11p = s; 12} // p has 2 children 13 14//獲取刪除節點的替代節點 15Entry<K,V> replacement = (p.left != null ? p.left : p.right); 16 17//如果存在替代節點,則進行替代。接著先刪除節點p,再進行刪除的平衡調整 18if (replacement != null) { 19//用替代節點替代刪除節點 20replacement.parent = p.parent; 21if (p.parent == null) 22root = replacement; 23else if (p == p.parent.left) 24p.parent.left= replacement; 25else 26p.parent.right = replacement; 27 28//刪除節點p 29p.left = p.right = p.parent = null; 30 31//如果刪除節點為黑色,必然影響樹的平衡,進行平衡調整 32if (p.color == BLACK) 33fixAfterDeletion(replacement); 34} else if (p.parent == null) { // return if we are the only node. 35root = null; 36} else {//如果不存在替代節點,不需要替代。接著先進行刪除的平衡調整,再刪除節點p 37//如果刪除節點為黑色,必然影響樹的平衡,進行平衡調整 38if (p.color == BLACK) 39fixAfterDeletion(p); 40 41//刪除節點p 42if (p.parent != null) { 43if (p == p.parent.left) 44p.parent.left = null; 45else if (p == p.parent.right) 46p.parent.right = null; 47p.parent = null; 48} 49} 50} 51 52//注意該方法執行的前提是t節點有左右節點,所以返回的是右子樹裡最左邊的非空 53static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) { 54if (t == null) 55return null; 56else if (t.right != null) {//存在右子樹 57Entry<K,V> p = t.right; 58while (p.left != null) //一直往左走 59p = p.left; 60return p; //返回右子樹裡最左的非空節點 61} else { 62Entry<K,V> p = t.parent; 63Entry<K,V> ch = t; 64while (p != null && ch == p.right) { 65ch = p; 66p = p.parent; 67} 68return p; 69} 70} 複製程式碼
通過上述邏輯我們知道,刪除節點分為下面幾種情況:
- 有左右孩子節點
- 只有一個孩子節點
- 無孩子節點(葉子節點)【情況最為複雜,後面會詳細講解】
2.2、程式碼整理成流程圖:

2.3、示例:
-
有左右孩子,且無替代節點:【先調整後刪除的流程】
圖注:有左右孩子但無替代節點
無替代節點,說明後繼節點走到了葉子節點。需要調整說明後繼節點為黑色,正如上圖所示。此時就轉化為對於葉子節點10的刪除操作了,處理過程請往後看。
-
有左右孩子,且有替代節點:【先刪除後調整的流程】
圖注:有左右孩子且有替代節點
有替代節點,說明後繼節點未走到葉子節點,那麼後繼節點肯定只有一個孩子節點,否則必可以走到葉子節點(此處可以畫圖體會體會)。一個節點只有一個孩子節點,那麼只有一種可能,就是節點為黑,孩子為紅,否則樹不平衡,正如上圖所示。此時,替代節點為紅色,調整邏輯就很簡單了,就是將該紅色節點改成黑色節點即可。
-
只有一個孩子節點,必有替代節點:【先刪除後調整的流程】
圖注:只有一個孩子->必有替代節點
只有一個孩子節點,替代節點獲取的規則就是要麼左節點,要麼右節點,所以必有替代節點。一個節點只有一個孩子節點,那麼只有一種可能,就是節點為黑,孩子為紅,否則樹不平衡,正如上圖所示。此時,替代節點為紅色,調整邏輯就很簡單了,就是將該紅色節點改成黑色節點即可。
2.4、總結:
通過上述的分析,凡是先進行刪除,後進行調整的情況,其調整邏輯都很簡單,就是隻需要將節點顏色從紅色變成黑色即可。先進行調整,後進行刪除的情況,才是邏輯最為複雜的,請繼續往下看。
3、對樹進行刪除後的調整:
1private void fixAfterDeletion(Entry<K,V> x) { 2//只要x不為根節點且為黑色,就需要調整 3while (x != root && colorOf(x) == BLACK) { 4//X是否為父親節點的左節點 5if (x == leftOf(parentOf(x))) { 6//獲取X右兄弟節點sib 7Entry<K,V> sib = rightOf(parentOf(x)); 8//X兄弟節點為紅色轉化為X兄弟節點為黑色的情況 9if (colorOf(sib) == RED) { 10//設定X兄弟節點為黑色 11setColor(sib, BLACK); 12//設定X父親節點為紅色 13setColor(parentOf(x), RED); 14//以X父親節點為中心進行左旋 15rotateLeft(parentOf(x)); 16//sib指向X的兄弟節點 17sib = rightOf(parentOf(x)); 18} 19 20//兄弟節點左右節點都為黑色 21if (colorOf(leftOf(sib))== BLACK && 22colorOf(rightOf(sib)) == BLACK) { 23//兄弟節點改成紅色 24setColor(sib, RED); 25//X指向父節點 26x = parentOf(x); 27} else { 28//X遠侄節點為黑色轉化為X遠侄節點為紅色的情況 29if (colorOf(rightOf(sib)) == BLACK) { 30//近侄節點改成黑色 31setColor(leftOf(sib), BLACK); 32//兄弟節點改成紅色 33setColor(sib, RED); 34//以兄弟節點為中心進行右旋 35rotateRight(sib); 36//sib指向X右兄弟節點 37sib = rightOf(parentOf(x)); 38} 39//設定sib為X父節點顏色 40setColor(sib, colorOf(parentOf(x))); 41//設定X父節點為黑色 42setColor(parentOf(x), BLACK); 43//設定遠侄節點為黑色 44setColor(rightOf(sib), BLACK); 45//以X父親節點為中心進行左旋 46rotateLeft(parentOf(x)); 47//X指向根,結束迴圈 48x = root; 49} 50} else { // symmetric 51//獲取X左兄弟節點sib 52Entry<K,V> sib = leftOf(parentOf(x)); 53//X兄弟節點為紅色轉化為X兄弟節點為黑色的情況 54if (colorOf(sib) == RED) { 55//設定X兄弟節點為黑色 56setColor(sib, BLACK); 57//設定X父親節點為紅色 58setColor(parentOf(x), RED); 59//以X父親節點為中心進行右旋 60rotateRight(parentOf(x)); 61//sib指向X的兄弟節點 62sib = leftOf(parentOf(x)); 63} 64 65//兄弟節點左右節點都為黑色 66if (colorOf(rightOf(sib)) == BLACK && 67colorOf(leftOf(sib)) == BLACK) { 68//兄弟節點改成紅色 69setColor(sib, RED); 70//X指向父節點 71x = parentOf(x); 72} else { 73//X遠侄節點為黑色轉化為X遠侄節點為紅色的情況 74if (colorOf(leftOf(sib)) == BLACK) { 75//近侄節點改成黑色 76setColor(rightOf(sib), BLACK); 77//兄弟節點改成紅色 78setColor(sib, RED); 79//以兄弟節點為中心進行左旋 80rotateLeft(sib); 81//sib指向X左兄弟節點 82sib = leftOf(parentOf(x)); 83} 84//設定sib為X父節點顏色 85setColor(sib, colorOf(parentOf(x))); 86//設定X父節點為黑色 87setColor(parentOf(x), BLACK); 88//設定遠侄節點為黑色 89setColor(leftOf(sib), BLACK); 90//以X父親節點為中心進行右旋 91rotateRight(parentOf(x)); 92//X指向根,結束迴圈 93x = root; 94} 95} 96} 97 98//設定X為黑色 99setColor(x, BLACK); 100} 複製程式碼
3.1、程式碼整理為流程圖:

3.2、優化的流程圖:

3.3、流程圖解析說明:
-
兄弟節點為黑色,遠侄節點為紅色(刪除節點X為葉子節點,且為黑色[如果為紅直接刪除即可])
圖注:兄弟為黑,遠侄為紅 -
兄弟節點為黑色,遠侄節點為黑色(刪除節點X為葉子節點,且為黑色[如果為紅直接刪除即可])
圖注:兄弟為黑,遠侄為黑 兄弟節點為黑色,並且刪除節點X為黑色,且為葉子節點->遠侄節點如果為黑色,必然是null節點,否則樹不平衡。
-
兄弟節點為黑色,遠侄近侄節點都為黑色(刪除節點X為葉子節點,且為黑色[如果為紅直接刪除即可])
圖注:兄弟為黑,遠近侄為黑 -
兄弟節點為紅色(刪除節點X為葉子節點,且為黑色[如果為紅直接刪除即可])
圖注:兄弟為紅 兄弟節點為紅色,X為黑色,且是葉子->遠侄節點和近侄節點必為null節點,否則樹不平衡。
3.4、示例:
-
兄弟節點為紅色
圖注:兄弟為紅 -
兄弟節點為黑色,遠侄節點為紅色
圖注:兄弟為黑,遠侄為紅 -
兄弟節點為黑色,遠侄節點為黑色
圖注:兄弟為黑,遠侄為黑 -
兄弟節點為黑色,遠侄近侄節點都為黑色
圖注:兄弟為黑,遠近侄為黑
總結
紅黑樹的刪除相對於插入而言,複雜了不少。但是隻要時刻記住五條性質,對於包含的每種場景動手去練習,我想理解它應該也不是難事。
最後,如果任何意見或建議,歡迎大家留言,如果覺得可以,記得點贊轉發哦。