紅黑樹移除節點node
上文詳細講解了紅黑樹的概念,紅黑樹的插入及旋轉操做,根據測試代碼創建起來的紅黑樹結構爲:算法
本文先研究一下紅黑樹的移除操做是如何實現的,移除操做比較複雜,具體移除的操做要進行幾回旋轉和移除的節點在紅黑樹中的位置有關,這裏也不特地按照旋轉次數選擇節點了,就找三種位置舉例演示紅黑樹移除操做如何進行:數據結構
首先來過一下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行的代碼是獲取待移除的節點Entry,作法很簡單,拿key與當前節點按指定算法作一個比較獲取cmp,cmp=0表示當前節點就是待移除的節點;cmp>0,取右子節點繼續比較;cmp<0,取左子節點繼續比較。spa
接着重點跟一下第7行的deleteEntry方法:code
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 }
用流程圖整理一下這裏的邏輯:blog
下面結合實際代碼來看下。繼承
移除根節點內存
根據上面的流程圖,根節點30左右子節點不爲空,所以要先選擇繼承者,選擇繼承者的流程爲:element
分點整理一下移除節點30作了什麼:
通過上述流程,移除根節點30以後的數據結構以下圖:
移除中間節點
接着看一下移除中間節點TreeMap是怎麼作的,這裏以移除節點70爲例,繼續分點整理一下移除節點70作了什麼:
整體流程和移除根節點差很少,惟一的區別是節點85是一個黑色節點,所以須要進行一次刪除數據修正操做。刪除數據修正實現爲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是否其父節點的左子節點。其流程圖爲:
從上圖中,首先能夠得出一個重要的結論:紅黑樹移除節點最多須要三次旋轉。
先看一下刪除數據修正以前的結構圖:
p指向右下角的黑色節點85,對此節點進行修正,上面的流程圖是p是父節點的左子節點的流程,這裏的p是父節點的右子節點,沒太大區別。
sib取父節點的左子節點即節點60,節點60是一個黑色節點,所以這裏不須要進行一次旋轉。
接着,sib的左右子節點不是黑色節點且sib的左子節點爲紅色節點,所以這裏只須要進行一次旋轉的流程:
通過這樣四步操做以後,紅黑樹的結構變爲:
最後一步的操做在fixAfterDeletion方法的外層,節點85的父節點不爲空,所以將節點85的父節點置空,最終移除節點70以後的數據結構爲:
移除最底下節點
最後看看移除最底下節點的場景,以移除節點85爲例,節點85根據代碼以節點p稱呼。
節點p沒有左右子節點,所以節點p不須要進行選擇繼承者的操做;一樣的因爲節點p沒有左右子節點,所以選擇出來的replacement爲null。
接着因爲replacement爲null可是節點p是一個黑色節點,黑色節點須要進行刪除修正流程:
這麼作以後,樹形結構變爲:
最後仍是同樣,回到fixAfterDeletion方法外層的代碼,將p的父節點置爲null,即節點p就不在當前數據結構中了,完成移除,紅黑樹最終的結構爲: