图解集合8:红黑树的移除节点操作
紅黑樹移除節(jié)點
上文詳細講解了紅黑樹的概念,紅黑樹的插入及旋轉(zhuǎn)操作,根據(jù)測試代碼建立起來的紅黑樹結構為:
本文先研究一下紅黑樹的移除操作是如何實現(xiàn)的,移除操作比較復雜,具體移除的操作要進行幾次旋轉(zhuǎn)和移除的節(jié)點在紅黑樹中的位置有關,這里也不特意按照旋轉(zhuǎn)次數(shù)選擇節(jié)點了,就找三種位置舉例演示紅黑樹移除操作如何進行:
- 移除根節(jié)點,例子就是移除節(jié)點30
- 移除中間節(jié)點,例子就是移除節(jié)點70
- 移除最底下節(jié)點,例子就是移除節(jié)點85
首先來過一下TreeMap的remove方法:
1 public V remove(Object key) { 2 Entry<K,V> p = getEntry(key); 3 if (p == null) 4 return null; 5 6 V oldValue = p.value; 7 deleteEntry(p); 8 return oldValue; 9 }第2行的代碼是獲取待移除的節(jié)點Entry,做法很簡單,拿key與當前節(jié)點按指定算法做一個比較獲取cmp,cmp=0表示當前節(jié)點就是待移除的節(jié)點;cmp>0,取右子節(jié)點繼續(xù)比較;cmp<0,取左子節(jié)點繼續(xù)比較。
接著重點跟一下第7行的deleteEntry方法:
1 private void deleteEntry(Entry<K,V> p) { 2 modCount++; 3 size--; 4 5 // If strictly internal, copy successor's element to p and then make p 6 // point to successor. 7 if (p.left != null && p.right != null) { 8 Entry<K,V> s = successor(p); 9 p.key = s.key; 10 p.value = s.value; 11 p = s; 12 } // p has 2 children 13 14 // Start fixup at replacement node, if it exists. 15 Entry<K,V> replacement = (p.left != null ? p.left : p.right); 16 17 if (replacement != null) { 18 // Link replacement to parent 19 replacement.parent = p.parent; 20 if (p.parent == null) 21 root = replacement; 22 else if (p == p.parent.left) 23 p.parent.left = replacement; 24 else 25 p.parent.right = replacement; 26 27 // Null out links so they are OK to use by fixAfterDeletion. 28 p.left = p.right = p.parent = null; 29 30 // Fix replacement 31 if (p.color == BLACK) 32 fixAfterDeletion(replacement); 33 } else if (p.parent == null) { // return if we are the only node. 34 root = null; 35 } else { // No children. Use self as phantom replacement and unlink. 36 if (p.color == BLACK) 37 fixAfterDeletion(p); 38 39 if (p.parent != null) { 40 if (p == p.parent.left) 41 p.parent.left = null; 42 else if (p == p.parent.right) 43 p.parent.right = null; 44 p.parent = null; 45 } 46 } 47 }用流程圖整理一下這里的邏輯:
下面結合實際代碼來看下。
?
移除根節(jié)點
根據(jù)上面的流程圖,根節(jié)點30左右子節(jié)點不為空,因此要先選擇繼承者,選擇繼承者的流程為:
分點整理一下移除節(jié)點30做了什么:
經(jīng)過上述流程,移除根節(jié)點30之后的數(shù)據(jù)結構如下圖:
?
移除中間節(jié)點
接著看一下移除中間節(jié)點TreeMap是怎么做的,這里以移除節(jié)點70為例,繼續(xù)分點整理一下移除節(jié)點70做了什么:
總體流程和移除根節(jié)點差不多,唯一的區(qū)別是節(jié)點85是一個黑色節(jié)點,因此需要進行一次刪除數(shù)據(jù)修正操作。刪除數(shù)據(jù)修正實現(xiàn)為fixAfterDeletion方法,它的源碼:
1 private void fixAfterDeletion(Entry<K,V> x) { 2 while (x != root && colorOf(x) == BLACK) { 3 if (x == leftOf(parentOf(x))) { 4 Entry<K,V> sib = rightOf(parentOf(x)); 5 6 if (colorOf(sib) == RED) { 7 setColor(sib, BLACK); 8 setColor(parentOf(x), RED); 9 rotateLeft(parentOf(x)); 10 sib = rightOf(parentOf(x)); 11 } 12 13 if (colorOf(leftOf(sib)) == BLACK && 14 colorOf(rightOf(sib)) == BLACK) { 15 setColor(sib, RED); 16 x = parentOf(x); 17 } else { 18 if (colorOf(rightOf(sib)) == BLACK) { 19 setColor(leftOf(sib), BLACK); 20 setColor(sib, RED); 21 rotateRight(sib); 22 sib = rightOf(parentOf(x)); 23 } 24 setColor(sib, colorOf(parentOf(x))); 25 setColor(parentOf(x), BLACK); 26 setColor(rightOf(sib), BLACK); 27 rotateLeft(parentOf(x)); 28 x = root; 29 } 30 } else { // symmetric 31 Entry<K,V> sib = leftOf(parentOf(x)); 32 33 if (colorOf(sib) == RED) { 34 setColor(sib, BLACK); 35 setColor(parentOf(x), RED); 36 rotateRight(parentOf(x)); 37 sib = leftOf(parentOf(x)); 38 } 39 40 if (colorOf(rightOf(sib)) == BLACK && 41 colorOf(leftOf(sib)) == BLACK) { 42 setColor(sib, RED); 43 x = parentOf(x); 44 } else { 45 if (colorOf(leftOf(sib)) == BLACK) { 46 setColor(rightOf(sib), BLACK); 47 setColor(sib, RED); 48 rotateLeft(sib); 49 sib = leftOf(parentOf(x)); 50 } 51 setColor(sib, colorOf(parentOf(x))); 52 setColor(parentOf(x), BLACK); 53 setColor(leftOf(sib), BLACK); 54 rotateRight(parentOf(x)); 55 x = root; 56 } 57 } 58 } 59 60 setColor(x, BLACK); 61 }方法第3行~第30行與第30行~第57行是對稱的,因此只分析一下前半部分也就是第3行~第30行的代碼。第三行的代碼"x == leftOf(parentOf(x))"很顯然判斷的是x是否其父節(jié)點的左子節(jié)點。其流程圖為:
從上圖中,首先可以得出一個重要的結論:紅黑樹移除節(jié)點最多需要三次旋轉(zhuǎn)。
先看一下刪除數(shù)據(jù)修正之前的結構圖:
p指向右下角的黑色節(jié)點85,對此節(jié)點進行修正,上面的流程圖是p是父節(jié)點的左子節(jié)點的流程,這里的p是父節(jié)點的右子節(jié)點,沒太大區(qū)別。
sib取父節(jié)點的左子節(jié)點即節(jié)點60,節(jié)點60是一個黑色節(jié)點,因此這里不需要進行一次旋轉(zhuǎn)。
接著,sib的左右子節(jié)點不是黑色節(jié)點且sib的左子節(jié)點為紅色節(jié)點,因此這里只需要進行一次旋轉(zhuǎn)的流程:
經(jīng)過這樣四步操作之后,紅黑樹的結構變?yōu)?#xff1a;
最后一步的操作在fixAfterDeletion方法的外層,節(jié)點85的父節(jié)點不為空,因此將節(jié)點85的父節(jié)點置空,最終移除節(jié)點70之后的數(shù)據(jù)結構為:
?
移除最底下節(jié)點
最后看看移除最底下節(jié)點的場景,以移除節(jié)點85為例,節(jié)點85根據(jù)代碼以節(jié)點p稱呼。
節(jié)點p沒有左右子節(jié)點,因此節(jié)點p不需要進行選擇繼承者的操作;同樣的由于節(jié)點p沒有左右子節(jié)點,因此選擇出來的replacement為null。
接著由于replacement為null但是節(jié)點p是一個黑色節(jié)點,黑色節(jié)點需要進行刪除修正流程:
這么做之后,樹形結構變?yōu)?#xff1a;
最后還是一樣,回到fixAfterDeletion方法外層的代碼,將p的父節(jié)點置為null,即節(jié)點p就不在當前數(shù)據(jù)結構中了,完成移除,紅黑樹最終的結構為:
?
轉(zhuǎn)載于:https://www.cnblogs.com/xrq730/p/6882018.html
總結
以上是生活随笔為你收集整理的图解集合8:红黑树的移除节点操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hdu 4925 Apple Tree-
- 下一篇: .net core 集成 autofac