对于红黑树的删除,看了数据结构的书,也看了不少网上的讲解和实现,但都不满意。不少讲解都是囫囵吞枣,知其然,不知其因此然,讲的晦涩难懂。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 }