红黑树(六):删除最大键

1. 删除最大键 - deleteMax()

deleteMax()的删除思路和deleteMin()是相似的,所以我们首先来看看所有的删除情况,但是对于deleteMax(),我们需要注意两点:

  1. 这次我们不断检查右子树,同时保证经过的当前结点不是2-结点;
  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. 特别感谢

  1. 感谢 @SENNICHEN 制作系列文章封面图

5. 免责声明

※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;


在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值