红黑树(上)

一、前言

在学习红黑树之前需要学习B树,它们之间有着密切的关系,因为红黑树可以和4阶B树完美匹配。我的理解是4阶B树里最多含4个分叉。

我们会发现B树有一些特点:

1.比较矮

2.平衡

3.拥有二叉搜索树的一些性质

4.一个节点能存储多个元素

一个m阶的B树具有以下性质:

根节点:1<= x <= m-1

非根节点:[m/2] - 1 <= x <= m-1

所以4阶B树的根节点个数 1<= x <= 3 , 非根节点的个数:1<= x <= 3, [ ] 这个符号代表的是向上取整。 

 

既然是这样那么添加删除的时候,就会出现问题呗!

添加的时候可能会出现上溢,解决办法:找到该节点的最中间的元素,然后该节点就一分为二。这个例子中就是把95放到父节点中去,而90和98、100就变成了两个节点。

删除的时候可能会发生下溢:

解决办法:

分两种情况:

当删除的节点是非叶子节点时,和二叉搜索树的做法是一样的,找到该节点的前驱节点或者后继节点,然后替换掉60,最后删除55或者70,你会发现最后删除的节点还是叶子节点。

当删除的节点是叶子节点时,当兄弟节点个数大于1时,比如删除50,做法就很简单,让根节点52左旋转就行了;当兄弟节点只有一个节点时,例如:删除10,那么根节点20就需要和子节点合并。

二、红黑树的添加

首先一颗树是红黑树必须满足5个性质:

1.节点的颜色必须是红色或者黑色

2.根节点是黑色

3.叶子节点是黑色

4.红色节点的父节点和叶子节点必须是黑色

5.从根节点到任意一个叶子节点所包含的黑色节点个数是一样的。

当一颗二叉搜索树满足以上性质后,我们就说这个树是红黑树,且是平衡的。

红黑树的等价变换:

 

下面就来分析红黑树的添加:

我们知道添加的节点一定是叶子节点,这个非常重要。

建议新添加的节点默认为 RED,这样能够让红黑树的性质尽快满足(性质 1、2、3、5 都满足,性质 4 不一定)

那么问题来了,添加一共有多少种情况呢?答案是12,从上图中就能数出来(17的左右孩子,33的左右孩子...),但实际我们会将这12种进行分类。

第一种情况:添加的节点的父节点是黑色节点,不需要做任何处理,因为它还是红黑树。

第二种情况:当添加的节点是52或60时,我们会对它们的祖父节点进行旋转,如果是52就对46进行左旋转,如果是60就对76进行右旋转,旋转的代码我们在AVL树中已经写过了。

 

第三种情况:当添加的节点是48,我们就对50进行右旋,记住一点对一个节点进行旋转,那么它的高度一定是变小,然后再对46进行左旋。你会发现学会了旋转,再学红黑树就没有那么难了;同理当添加的节点是74是,就对72进行左旋,然后对76进行右旋。

第四种情况:当添加的节点是10、20、30、36时,我们做以下处理:

 

添加方法:

package 红黑树;

import java.util.Comparator;

public class RBTree<E>{

    private static final boolean RED = false;
    private static final boolean BLACK = true;

    private int size;
    private RBNode<E> root;
    private Comparator<E> comparator;

    private static class RBNode<E>{
        boolean color = RED;
        E element;
        RBNode<E> left;
        RBNode<E> right;
        RBNode<E> parent;

        public RBNode(E element,RBNode<E> parent){
            this.element = element;
            this.parent = parent;
        }
    }

    public void add(E element) {
        if(element == null) return;

        // 添加第一个节点
        if (root == null) {
            root = new RBNode<>(element, null);
            size++;
            afterAdd(root);
            return;
        }

        // 添加的不是第一个节点
        // 找到父节点
        RBNode<E> parent = root;
        RBNode<E> node = root;
        int cmp = 0;
        while (node != null){
            cmp = comparator.compare(element, node.element);
            parent = node;
            if (cmp > 0) {
                node = node.right;
            } else if (cmp < 0) {
                node = node.left;
            } else { // 相等
                node.element = element;
                return;
            }
        }
        // 看看插入到父节点的哪个位置
        RBNode<E> newNode = new RBNode<>(element, parent);
        if (cmp > 0) {
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }
        afterAdd(newNode);
        size++;
    }


    protected void afterAdd(RBNode<E> node) {
        RBNode<E> parent = node.parent;

        // 添加的是根节点 或者 上溢到达了根节点
        if (parent == null) {
            black(node);
            return;
        }

        // 如果父节点是黑色,直接返回
        if (isBlack(parent)) return;

        // 叔父节点
        RBNode<E> uncle = sibling(node);
        // 祖父节点
        RBNode<E> grand = red(parent.parent);
        if (isRed(uncle)) { // 叔父节点是红色【B树节点上溢】
            black(parent);
            black(uncle);
            // 把祖父节点当做是新添加的节点
            afterAdd(grand);
            return;
        }

        // 叔父节点不是红色
        if (isLeftChild(parent)) { // L
            if (isLeftChild(node)) { // LL
                black(parent);
            } else { // LR
                black(node);
                lefTurn(parent);
            }
            rightTurn(grand);
        } else { // R
            if (isLeftChild(node)) { // RL
                black(node);
                rightTurn(parent);
            } else { // RR
                black(parent);
            }
            lefTurn(grand);
        }
    }

    private void lefTurn(RBNode<E> grand) {
        RBNode<E> parent = grand.right; //节点p
        RBNode<E> child = parent.left; //子树T1
        grand.right = child;
        parent.left = grand;
        //让p成为根节点
        parent.parent = grand.parent;
        if(grand == grand.parent.left){
            grand.parent.left = parent;
        }else if(grand == grand.parent.right){
            grand.parent.right = parent;
        }else{
            root = parent;
        }
        if(child!=null){
            child.parent = grand; //更新t1的parent
        }
        grand.parent = parent; //更新grand的parent
    }


    private void rightTurn(RBNode<E> grand) {
        RBNode<E> parent = grand.left; //节点p
        RBNode<E> child = parent.right; //子树T2
        grand.left = child;
        parent.right = grand;
        //让p成为根节点
        parent.parent = grand.parent;
        if(grand == grand.parent.left){
            grand.parent.left = parent;
        }else if(grand == grand.parent.right){
            grand.parent.right = parent;
        }else{
            root = parent;
        }

        //更新parent属性
        if(child!=null){
            child.parent = grand;
        }
        grand.parent = parent;

    }


    private RBNode<E> color(RBNode<E> node, boolean color) {
        if (node == null) return node;
        node.color = color;
        return node;
    }

    private RBNode<E> red(RBNode<E> node) {
        return color(node, RED);
    }

    private RBNode<E> black(RBNode<E> node) {
        return color(node, BLACK);
    }

    private boolean colorOf(RBNode<E> node) {
        return node == null ? BLACK : node.color;
    }

    private boolean isBlack(RBNode<E> node) {
        return colorOf(node) == BLACK;
    }

    private boolean isRed(RBNode<E> node) {
        return colorOf(node) == RED;
    }

    public boolean isLeftChild(RBNode<E> node) {
        return node.parent != null && node == node.parent.left;
    }

    public boolean isRightChild(RBNode<E> node) {
        return node.parent != null && node == node.parent.right;
    }

    public RBNode<E> sibling(RBNode<E> node) {
        if(node.parent == null) return null;
        if (isLeftChild(node)) {
            return node.parent.right;
        }

        if (isRightChild(node)) {
            return node.parent.left;
        }

        return null;
    }


}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值