红黑树(四):插入实现

红黑树(四):插入实现

1. 插入实现

首先,我们先来看看插入相关的所有方法:

/**
 * Balance Case One:
 * root's left child is RED and
 * the right child is BLACK
 * */

private boolean ifBalanceCaseOne( RedBlackTreeNode root ) {
    return isRed( root.right ) && !isRed( root.left );
}

/**
 * Balance Case Two:
 * root's left child is RED
 * and the left child's left child is RED
 * */

private boolean ifBalanceCaseTwo( RedBlackTreeNode root ) {
    return isRed( root.left ) && isRed( root.left.left );
}

/**
 * Balance Case Three:
 * root's left child is BLACK and
 * the right child is RED
 * */

private boolean ifBalanceCaseThree( RedBlackTreeNode root ) {
    return isRed( root.left ) && isRed( root.right );
}

/**
 * balance this R-B tree
 * */

private RedBlackTreeNode balance( RedBlackTreeNode root ) {
    if ( ifBalanceCaseOne( root ) ) root = rotateLeft( root );
    if ( ifBalanceCaseTwo( root ) ) root = rotateRight( root );
    if ( ifBalanceCaseThree( root ) ) flipColors( root, false );

    return ( RedBlackTreeNode ) updateSize( root );
}

/**
 * put key -> val into this R-B tree
 * */

public void put( K key, V val ) {
    RedBlackTreeNode root = put( ( RedBlackTreeNode ) this.root, key, val );
    root.color = BLACK;
    this.root = root;
}

// this part of code is very similar to
// put( MapTreeNode<K, V> root, K key, V val ).
private RedBlackTreeNode put( RedBlackTreeNode root, K key, V val ) {
    // base case, attach the new node to this position
    if ( root == null ) return new RedBlackTreeNode( ID++, key, val, RED );

    int res = compareKeys( root, key );
    // the node should be attached in the left subtree
    if ( res > 0 ) root.left = put( ( RedBlackTreeNode ) root.left, key, val );
    // the node should be attached in the right subtree
    else if ( res < 0 ) root.right = put( ( RedBlackTreeNode ) root.right, key, val );
    // added before, update value
    else root.val = val;

    // update size and restore this R-B tree
    return balance( root );
}

接下来,我们来解析相关代码,让大家更好的理解插入的实现。

首先我们看看put()的整体代码结构:

/**
 * put key -> val into this R-B tree
 * */

public void put( K key, V val ) {
    RedBlackTreeNode root = put( ( RedBlackTreeNode ) this.root, key, val );
    root.color = BLACK;
    this.root = root;
}

// this part of code is very similar to
// put( MapTreeNode<K, V> root, K key, V val ).
private RedBlackTreeNode put( RedBlackTreeNode root, K key, V val ) {
    // base case, attach the new node to this position
    if ( root == null ) return new RedBlackTreeNode( ID++, key, val, RED );

    int res = compareKeys( root, key );
    // the node should be attached in the left subtree
    if ( res > 0 ) root.left = put( ( RedBlackTreeNode ) root.left, key, val );
    // the node should be attached in the right subtree
    else if ( res < 0 ) root.right = put( ( RedBlackTreeNode ) root.right, key, val );
    // added before, update value
    else root.val = val;

    // update size and restore this R-B tree
    return balance( root );
}

我们先忽略balance(),大家应该能发现红黑树的put()和BST的put()其实是差不多的,都是沿着root,搜索需要插入的位置,然后插入新键。这也是为什么红黑树结构里面没有get(),因为红黑树的get()和BST是一模一样的,直接继承沿用即可。

那么,整个put()实现最关键的就是balance()里面的了。没错,这个方法的意义就在于,当我们再某个位置插入一个新键,我们需要对树进行自平衡。

/**
 * Balance Case One:
 * root's left child is RED and
 * the right child is BLACK
 * */

private boolean ifBalanceCaseOne( RedBlackTreeNode root ) {
    return isRed( root.right ) && !isRed( root.left );
}

/**
 * Balance Case Two:
 * root's left child is RED
 * and the left child's left child is RED
 * */

private boolean ifBalanceCaseTwo( RedBlackTreeNode root ) {
    return isRed( root.left ) && isRed( root.left.left );
}

/**
 * Balance Case Three:
 * root's left child is BLACK and
 * the right child is RED
 * */

private boolean ifBalanceCaseThree( RedBlackTreeNode root ) {
    return isRed( root.left ) && isRed( root.right );
}

/**
 * balance this R-B tree
 * */

private RedBlackTreeNode balance( RedBlackTreeNode root ) {
    if ( ifBalanceCaseOne( root ) ) root = rotateLeft( root );
    if ( ifBalanceCaseTwo( root ) ) root = rotateRight( root );
    if ( ifBalanceCaseThree( root ) ) flipColors( root, false );

    return ( RedBlackTreeNode ) updateSize( root );
}

对于这个自平衡实现,也是非常的简单。大家还记得上一节的这张插入关系图么:

在这里插入图片描述

其实上面的自平衡代码就是对这张关系图的直接实现。我们知道case 2和case 3经过变换,可以变成case 1,case 1经过染色就完成了这个局部的自平衡操作,接下来我们只需要递归向上自平衡即可。

所以我们只需要依次检查是不是需要进行上面三种情况的自平衡,检查完以后,更新一下结点数即可。这就是balance()里面代码的意思:

if ( ifBalanceCaseOne( root ) ) root = rotateLeft( root );
if ( ifBalanceCaseTwo( root ) ) root = rotateRight( root );
if ( ifBalanceCaseThree( root ) ) flipColors( root, false );

return ( RedBlackTreeNode ) updateSize( root );

而且我们也不会担心出现没有自平衡的情况,因为上面的关系图已经说明了:如果遇到case 3 -> 染色后自平衡,如果遇到case 2 -> 右旋 -> case 3,如果遇到case 1 -> 左旋 -> case 2。所以不会出现经过检查,但该树局部没有自平衡的情况。

到此,红黑树的插入操作也讲解完毕。接下来,我们将进入本系列最难的 删除 - delete() 讲解。不过也不用太担心哒,慢慢来,如果第一遍不是很能理解,可以休息一下,再多看几遍哒。当时博主完全弄懂删除操作,用了4-5天,哈哈哈~


上一节:红黑树(三):插入·续
下一节:红黑树(五):删除最小键
系列汇总:超详细!红黑树详解文章汇总(含代码)

2. 特别感谢

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

3. 免责声明

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


在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值