红黑树(六):删除最大键
1. 删除最大键 - deleteMax()
deleteMax()的删除思路和deleteMin()是相似的,所以我们首先来看看所有的删除情况,但是对于deleteMax(),我们需要注意两点:
- 这次我们不断检查右子树,同时保证经过的当前结点不是2-结点;
- 为了简略分析,当root为3-结点,我们省略了其最右子树依然3-结点的情况,这些情况依然可以用相似的方法进行转换,最终转换成更为简单的case,大家可以自己试着转化一下,加深对deleteMax()思路的理解;
那么,来看看分情况讨论的图例吧:
通过观察,我们可以得到deleteMax()相关的结论:
只要是当前结点是一个3-结点,我们就对其右旋,因为deleteMax()需要删除右子树,但3-结点的红链接是在左边,所以需要翻转一下,保证红链接在右侧;Case 7, 8, 9, 10都印证了这一点;
同样,deleteMax()也有和deleteMin()相似的moveRedRight():
按道理当 root.right == BLACK,就进行4-结点转化,但是我们观察Case 5 和 Case 6 发现,只有同时 root.right.left == BALCK才需要转换;
至于转换过程,我们先反转染色,然后通过观察Case 4,发现当 root.left.left == RED 的时候,右旋root,将第二层的红链接向上提升。
2. 实现deleteMax()
接着我们结合图例来分析一下代码,整体的deleteMax():
/**
* delete the maximum key -> value in this R-B tree
* */
public void deleteMax() {
// the following commented out code is from the textbook,
// but from my point of view, they're redundant
// if ( !isRed( ( RedBlackTreeNode ) root.left ) &&
// !isRed( ( RedBlackTreeNode ) root.right ) )
// ( ( RedBlackTreeNode ) root ).color = RED;
// the root is null, i.e the tree is empty,
// which is missed by the textbook
if ( isEmpty() ) return;
RedBlackTreeNode root = deleteMax( ( RedBlackTreeNode ) this.root );
this.root = root;
if ( !isEmpty() ) root.color = BLACK;
}
private RedBlackTreeNode deleteMax( RedBlackTreeNode root ) {
// handle case 2
if ( isRed( root.left ) )
root = rotateRight( root );
// base case, this node is the greatest one in the tree
// and it's also a leaf node in this R-B tree,
// so just return null, instead of return root.left,
// which is different from deleteMax() for BST
if ( root.right == null ) return null;
// guarantee that every node
// we're traveling along right subtree
// is either 3-node or 4-node.
// differentiate case 4 and case 5,
// and handle case 5 more efficiently
if ( !isRed( root.right ) &&
!isRed( root.right.left ) )
root = moveRedRight( root );
// otherwise, look into the right subtree
root.right = deleteMax( ( RedBlackTreeNode ) root.right );
return balance( root );
}
private RedBlackTreeNode moveRedRight( RedBlackTreeNode root ) {
flipColors( root, true );
// handle case 4 more efficiently,
// since at this point, there is an extra red node on the left,
// we could move it to the right part of the tree
// but different from the code in the textbook,
// which is: !isRed( root.left.left )
if ( isRed( root.left.left ) ) {
root = rotateRight( root );
flipColors( root, false );
}
return root;
}
deleteMax()和deleteMin()在结构上非常相似,功能上也是相近的,只是操作变成了右侧;这里的代码同样注意两点:
1)教材上面关于4-结点变换是这样的:
if ( !isRed( root.left.left ) )
root = rotateRight( root );
和我们分析的情况是反的,但是根据Case 4,这里应该是检查 root.left.left 是不是红结点,而不是黑结点。虽然按照教材的代码,不会fail Case 4,但是大家可以用教材上面的代码去deleteMax()一下下面这个例子,会发现最后修复后的树违反了红黑树的定义1:
但如果用博主提供的代码,则会正确修复删除最大值的树;
2)教材上面的代码依然没有考虑空树的情况,所以需要进行修复,和deleteMin()一样:
// the root is null, i.e the tree is empty,
// which is missed by the textbook
if ( isEmpty() ) return;
现在,我们已经讲解完deleteMax() 和 deleteMin(),其实红黑树的删除操作大家已经有了非常好的认识,那么接下来的delete()就不会显得那么难了,因为它的思想是基于BST的delete()和上一节的deleteMin()来共同完成的。加油啦,就差最后一步就能掌握红黑树啦~
上一节:红黑树(五):删除最小键
下一节:红黑树(七):删除和性能分析
系列汇总:超详细!红黑树详解文章汇总(含代码)
3. 教材提供的deleteMax()代码
说实话,不建议大家看中文版上面的代码,各种魔改不说,好多边界案例都不考虑的,比如空树的情况,但是英文原版代码就没有这个问题哦。所以这里推荐英文原版完整红黑树代码:传送门
3.1 英文原版
/**
* Removes the largest key and associated value from the symbol table.
* @throws NoSuchElementException if the symbol table is empty
*/
public void deleteMax() {
if (isEmpty()) throw new NoSuchElementException("BST underflow");
// if both children of root are black, set root to red
if (!isRed(root.left) && !isRed(root.right))
root.color = RED;
root = deleteMax(root);
if (!isEmpty()) root.color = BLACK;
// assert check();
}
// delete the key-value pair with the maximum key rooted at h
private Node deleteMax(Node h) {
if (isRed(h.left))
h = rotateRight(h);
if (h.right == null)
return null;
if (!isRed(h.right) && !isRed(h.right.left))
h = moveRedRight(h);
h.right = deleteMax(h.right);
return balance(h);
}
// Assuming that h is red and both h.right and h.right.left
// are black, make h.right or one of its children red.
private Node moveRedRight(Node h) {
// assert (h != null);
// assert isRed(h) && !isRed(h.right) && !isRed(h.right.left);
flipColors(h);
if (isRed(h.left.left)) {
h = rotateRight(h);
flipColors(h);
}
return h;
}
3.2 中文教课书
4. 特别感谢
- 感谢 @SENNICHEN 制作系列文章封面图
5. 免责声明
※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;