對於紅黑樹的刪除,看了數據結構的書,也看了不少網上的講解和實現,但都不滿意。不少講解都是囫圇吞棗,知其然,不知其因此然,講的晦澀難懂。node
紅黑樹是平衡二叉樹的一種,其刪除算法是比較複雜的,由於刪除後還要保持紅黑樹的特性。紅黑樹的特性以下:算法
所以,從紅黑樹最基礎的特性出發,拋開教科書和網上的算法,畫了無數張圖,分析了多種可能的狀況之後,通過概括提煉,實現了不一樣於教科書上的刪除算法。網絡
通過屢次畫圖證實之後,筆者發現,紅黑樹的刪除算法不是惟一的,無論如何調整,只要保證刪除後仍是一顆紅黑樹便可。數據結構
所以,筆者實現的 刪除思路和算法以下:ide
1. 刪除轉移:(這部分是大路貨,不是本身實現的)ui
刪除轉移的目的是爲了簡化刪除操做,更是爲了簡化修復操做。由於刪除轉移後,最終待刪除的節點最多隻會有一個非空孩子。編碼
2. 刪除後修復:spa
2.1 簡單的狀況:code
對於以上兩種簡單的狀況,作個說明:根據紅黑樹特性,非空子節點必定爲紅色節點,不然將違反特性;根據紅黑樹特性,在刪除前,一顆紅黑樹不可能出現如下幾種狀況:blog
(圖片來自網絡,感謝原做者。)
2.2 複雜的狀況:刪除後須要修復的。
只有當被刪除的節點爲黑色葉子節點時,致使該節點所在的分支,少了一個黑色節點,樹再也不平衡,所以須要修復。
修復的總體思路是:
掌握了總體思路之後,就可編碼實現了,編碼中用了一些小技巧,合併了一些狀況,代碼比較簡單易懂,閱讀者能夠根據代碼的狀況本身畫圖證實:
說明:代碼爲dart語言實現,dart語法基本與Java一致,不清楚的地方能夠參考:
https://www.dartlang.org/guides/language/language-tour
1 // 刪除 2 bool delete(E value) { 3 var node = find(value); 4 if (node == null) return false; 5 _delete(node); 6 _nodeNumbers--; 7 return true; 8 } 9 10 // 刪除轉移 並修復 11 void _delete(RBTNode<E> d) { 12 if (d.left != null && d.right != null) { 13 var s = _successor(d); 14 d.value = s.value; 15 d = s; 16 } 17 18 var rp = d.left ?? d.right; 19 rp?.parent = d.parent; 20 if (d.parent == null) 21 _root = rp; 22 else if (d == d.parent.left) 23 d.parent.left = rp; 24 else 25 d.parent.right = rp; 26 27 if (rp != null) 28 rp.paintBlack(); 29 else if (d.isBlack && d.parent != null) 30 _fixAfterDelete(d.parent, d.parent.left == null); 31 } 32 33 RBTNode<E> _successor(RBTNode<E> d) => 34 d.right != null ? _minNode(d.right) : d.left; 35 36 RBTNode<E> _minNode(RBTNode<E> r) => r.left == null ? r : _minNode(r.left); 37 38 // fix up after delete 39 void _fixAfterDelete(RBTNode<E> p, bool isLeft) { 40 var ch = isLeft ? p.right : p.left; 41 if (isLeft) { // 若是被刪除節點是父節點p的左分支; 42 if (p.isRed) { // 若是父節點爲紅,則兄弟節點ch必定爲黑; 43 if (ch.left != null && ch.left.isRed) { 44 p.paintBlack(); 45 _rotateRight(ch); 46 } 47 _rotateLeft(p); 48 } else if (ch.isRed) { // 兄弟節點爲紅,此時兄弟節點必定有兩個非空黑色子節點; 49 p.paintRed(); 50 ch.paintBlack(); 51 _rotateLeft(p); 52 _fixAfterDelete(p, true); // 變換爲父節點爲紅的狀況,遞歸處理; 53 } else if (ch.left != null && ch.left.isRed) { // 父、兄均爲黑,兄有紅色左孩子; 54 ch.left.paintBlack(); 55 _rotateRight(ch); 56 _rotateLeft(p); 57 } else { // 父兄均爲黑,將父分支左右均減小一個黑節點,而後遞歸向上處理; 58 p.paintRed(); 59 _rotateLeft(p); 60 if (ch.parent != null) _fixAfterDelete(ch.parent, ch == ch.parent.left); 61 } 62 } else { // symmetric 63 if (p.isRed) { 64 if (ch.right != null && ch.right.isRed) { 65 p.paintBlack(); 66 _rotateLeft(ch); 67 } 68 _rotateRight(p); 69 } else if (ch.isRed) { 70 p.paintRed(); 71 ch.paintBlack(); 72 _rotateRight(p); 73 _fixAfterDelete(p, false); 74 } else if (ch.right != null && ch.right.isRed) { 75 ch.right.paintBlack(); 76 _rotateLeft(ch); 77 _rotateRight(p); 78 } else { 79 p.paintRed(); 80 _rotateRight(p); 81 if (ch.parent != null) _fixAfterDelete(ch.parent, ch == ch.parent.left); 82 } 83 } 84 }
旋轉操做的代碼:
1 void _rotateLeft(RBTNode<E> node) { 2 var r = node.right, p = node.parent; 3 r.parent = p; 4 if (p == null) 5 _root = r; 6 else if (p.left == node) 7 p.left = r; 8 else 9 p.right = r; 10 11 node.right = r.left; 12 r.left?.parent = node; 13 r.left = node; 14 node.parent = r; 15 } 16 17 void _rotateRight(RBTNode<E> node) { 18 var l = node.left, p = node.parent; 19 l.parent = p; 20 if (p == null) 21 _root = l; 22 else if (p.left == node) 23 p.left = l; 24 else 25 p.right = l; 26 27 node.left = l.right; 28 l.right?.parent = node; 29 l.right = node; 30 node.parent = l; 31 }