红黑树的删除和BST的删除也是类似的,区别在于红黑树删除后需要保持性质。这就同样需要在删除过后进行调整。而红黑树的删除主要是对于单支黑节点(即一个黑节点只有左子树或只有右子树)的操作。
因为删除红色叶节点对于红黑树的性质没有影响,删除黑色叶节点因为存在着sentinel节点,所以可以归入删除黑色internal节点的情况。
而对于删除internal节点,则分为左右子树均非空的节点,单支黑节点,单支红节点三种情况:
如果该internal节点左右子树均非空,则像BST那样在右子树中找中缀后继节点,用后继节点的值来替换待删除的internal节点的值,internal节点的其余成员值保持不变,就相当于删除了该节点,同时继续对后继节点进行删除操作,实际上就可以归入删除单支节点的情况;
如果该internal节点为单支红节点,则不论其子节点是红是黑,均不符合红黑树的性质,所以实际上并不存在此种情况;
如果该internal节点为单支黑节点,那么像BST那样将其子树接上,就相当于是删除了该节点。而这样一来,因为删除了一个黑节点,对于红黑树的第五个性质造成了破坏,这里就需要对红黑树进行调整。
因此实际上在具体实现中我们仅在被删除节点为黑节点时进行调整,所以在具体实现的PtrRBT RBDelete(PtrRBT T, ElementType Val)中,需要用一个临时变量DeleteNode来记录待删除节点,并在删除执行完之后判断待删除节点的颜色,以便确定是否需要调用PtrRBT RBDeleteFixUp(PtrRBT T, PtrRBTNode FixUpNode)函数进行调整:
if(Black == DeleteNode->Color){ T = RBDeleteFixUp(T, FixUpNode); }
而以上所说的调整则是需要根据被删除的单支黑节点的子节点的Color来判断的。如果其子节点是黑色的,则有四种不同的情况,在RBDeleteFixUp函数的具体实现中需要进入一个while循环来处理,如果其子节点是红色的,则无需进入循环,直接执行循环后的FixUpNode->Color = Black;,就是将其子节点的Color变为黑色,相当于增加了一个黑节点来消除删除一个黑节点对红黑树性质的影响。
对于四种不同情况,《算法导论》上介绍的很明白了,下面主要说明Case2、4的情况,并通过注释来介绍Case1、3和具体实现:
当然需要特别注意的是,所谓的待调整节点其实就是沿待调整节点比沿待调整节点的Sibling节点至叶节点的Black节点数少1,所以在调整过程中我们只在待调整节点为黑且不为根节点时做调整,因为待调整节点为红时仅需将其置为黑既能保持红黑树性质,而待调整节点为根节点时相当于这一次删除最终使得整棵红黑树中根节点至叶节点的路径的Black节点数均减少1,但仍然符合红黑树性质。
Case Two此情况下,不论祖父节点B是何种颜色,均将父节点的Sibling节点D颜色置为Red,这样可以使上图情况2中沿A节点和D节点至叶节点的路径具有相同的Black节点数,但是却使得图中沿B节点至叶节点的路径的Black节点数减少1,这样相当于B节点等价于一个待调整节点,因为待调整节点的一个特点就是沿待调整节点比沿待调整节点的Sibling节点至叶节点的Black节点数少1,因此将待调整节点变为B即可,并继续循环,若B为红则跳出循环后将其颜色置为黑,若B为黑则继续循环判断是Case1、2、3、4中的何种情况。