红黑树的删除详解与思路分析——不一样于教科书上的算法(dart语言实现)

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

红黑树是平衡二叉树的一种,其删除算法是比较复杂的,由于删除后还要保持红黑树的特性。红黑树的特性以下:算法

    1. 节点是红色或黑色。
    2. 根是黑色。
    3. 全部叶子都是黑色(叶子是NIL节点)。
    4. 每一个红色节点必须有两个黑色的子节点。(从每一个叶子到根的全部路径上不能有两个连续的红色节点。)
    5. 从任一节点到其每一个叶子的全部简单路径都包含相同数目的黑色节点(简称黑高)。

所以,从红黑树最基础的特性出发,抛开教科书和网上的算法,画了无数张图,分析了多种可能的状况之后,通过概括提炼,实现了不一样于教科书上的删除算法。网络

通过屡次画图证实之后,笔者发现,红黑树的删除算法不是惟一的,无论如何调整,只要保证删除后仍是一颗红黑树便可。数据结构

所以,笔者实现的 删除思路和算法以下: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   }
相关文章
相关标签/搜索