【Java RBT红黑树 图解代码分析】简单理解

红黑树

  • 我会给大家从理论,图析,代码实现来看看红黑树,不难的,大家认真看

一、首先什么是红黑树?

我们可以看作特殊的AVL树,再通俗说:它的本质还是BST树,万变不离其宗。不过给AVL树加上特有的性质而已,红黑树只有左右旋转两种方式,和AVL树四种旋转相比简单,不过多了节点着色,我们首先来看看红黑树的五大性质:
1、节点都有颜色,不是黑色,就是红色
2、null表示黑色(叶子节点left和right的地址域)
3、根节点是黑色
4、不能出现连续的红色(连续:手拉手那种=-=)
5、从根节点到任意叶子节点路径上的黑色节点数量相同

我么可以从4,5条性质得到一个结论:红黑树节点子树高的不能超过低的二倍(验证:可以画左右两条路径,一条路径全黑色,另一条路径红黑相间,自己可以看看)

二、红黑树旋转图析

  • 图是我自己画的,直观易懂,我分析了AVL和红黑树的旋转,大家可以比较学习(红黑树旋转情况就只有我画的这几种情况,没别的了),加油!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

三、代码实现(红黑树的增删,代码上有我的注释)

大家看代码实现前,一定要认真看我上面的:图析(重要,直观易懂)

1.左右旋转代码
 /**
     * 以node为根节点进行左旋转
     * @param node
     */
    public void leftRotate(Entry<T> node) {
       Entry<T> child = node.right;
       node.right = child.left;
       if (child.left!=null){
           child.left.parent = node;
       }
       child.parent = node.parent;
       if (node.parent==null){
           root = child;
       }else {
           if (node.parent.left == node){
               node.parent.left = child;
           }else {
               node.parent.right = child;
           }
       }
      child.left = node;
       node.parent = child;
    }

    /**
     * 以node为根节点进行右旋转
     * 原理同左旋转
     * @param node
     */
    public void rightRotate(Entry<T> node) {
        Entry<T> child = node.left;
        node.left = child.right;
        if (child.right!=null){
            child.right.parent = node;
        }
        child.parent = node.parent;
        if (node.parent==null){
            root = child;
        }else {
            if (node.parent.left == node){
                node.parent.left = child;
            }else {
                node.parent.right = child;
            }
        }
        child.right = node;
        node.parent = child;
    }

2.创建红黑树类和节点类
public class RBTree <T extends Comparable<T>>{
    private Entry<T> root = null; //头节点
    static final boolean Red = false;
    static final boolean Black = true;
    static class Entry<E> {
        private E value;//数据域
        private Entry<E> left;//左孩子
        private Entry<E> right;//右孩子
        private Entry<E> parent;//父节点
        boolean color;//着色

        public Entry(E value, Entry<E> left,Entry<E> parent, Entry<E> right, boolean color) {
            this.value = value;
            this.left = left;
            this.right = right;
            this.parent = parent;
            this.color = color;
        }
        public Entry(E value) {//增加的节点都为红色
            this(value,null,null,null,Red);
        }
    }
3.插入调整代码
  /**
     * 插入后调整
     * @param node
     */
    public void insertAfter(Entry<T> node){
        //父亲,祖父都在,父亲节点是红
        while (node.parent!=null&&node.parent.parent!=null&&node.parent.color==Red){
            //左边判断(父亲是孩子的左孩子)
            if (node.parent ==node.parent.parent.left){//父节点是祖父节点的左孩子
                if (node.parent.parent.left!=null&&node.parent.parent.right.color==Red){//叔叔节点为红色
                    //那就把父亲和叔叔都变黑,祖父变红;更新node节点到祖父(向上判断)
                    node.parent.color = Black;
                    node.parent.parent.color = Red;
                    node.parent.parent.right.color = Black;
                    node = node.parent.parent;
                    continue;
                }
                if (node.parent.right == node){//叔叔节点为黑色,node是右孩子
                    leftRotate(node.parent);
                }
                //叔叔黑色且node是左孩子
                node.parent.color = Black;
                node.parent.parent.color = Red;
                rightRotate(node.parent.parent);
            }else {//父亲是孩子的右孩子
                if (node.parent.parent.left!=null&&node.parent.parent.left.color==Red){//叔叔节点为红色
                    //那就把父亲和叔叔都变黑,祖父变红;更新node节点到祖父(向上判断)
                    node.parent.color = Black;
                    node.parent.parent.color = Red;
                    node.parent.parent.left.color = Black;
                    node = node.parent.parent;
                    continue;
                }
                if (node.parent.left==node){//叔叔节点为黑,且node是左孩子
                    rightRotate(node.parent);
                }
                //叔叔黑色,且node右孩子
                node.parent.color = Black;
                node.parent.parent.color = Red;
                leftRotate(node.parent.parent);
            }
        }
        root.color = Black;
    }
4.插入代码
 /**
     * RBT树的插入
     * @param node
     */
    private void insert(Entry<T> node) {//就是非递归的BST插入操作,末尾加上调整操作就好了
        if (root == null){
            root = node;
        }
        Entry<T> cur = root;
        Entry<T> par = null;//保存cur的父节点
        while (cur!=null){
            if (cur.value.compareTo(node.value)>0){
                par = cur;
                cur = cur.left;
            }else if (cur.value.compareTo(node.value)<0){
                par = cur;
                cur = cur.right;
            }else {
                return;
            }
        }
        /**
         * 退出循环后,cur==null
         * 把新插入的叶子节点写入父节点相应的地址域中
         */
        if (par.value.compareTo(node.value)>0){
            par.left = node;
        }else {
            par.right = node;
        }
        node.parent = par;
        // 插入完后调整
        insertAfter(node);
    }
    public void insert(T data) {
        Entry<T> node = new Entry<>(data);
        if (node != null)
            insert(node);
    }
5.删除调整代码
 //删除调整代码
    public void removeAfter(Entry<T> child,Entry<T> parent){
        while ((child==null|| child.color==Black)&& child!=root){
            if (parent.left==child){//child是左孩子
                if (parent.right!=null&&parent.right.color==Red){//兄弟节点为红色
                    parent.right.color = Black;
                    parent.color = Red;
                    leftRotate(parent);
                }
                //兄弟和兄弟的两个孩子都为黑色
                if ((parent.right.left!=null || parent.right.left.color==Red)&&
                        (parent.right.right==null ||parent.right.right.color==Black)){
                    parent.right.color=Red;
                    child = parent;
                }else {//兄弟黑色,兄弟左孩子红色,右孩子黑色
                    if (parent.right.right==null||parent.right.left.color==Red){
                        parent.right.left.color = Black;
                        parent.right.color = Red;
                        rightRotate(parent.right);
                    }
                    //兄弟黑色,兄弟右孩子红
                    parent.right.color = parent.color;
                    parent.color = Black;
                    if (parent.right.right!=null){
                        parent.right.right.color = Black;
                    }
                    leftRotate(parent);
                    break;
                }
            }else {//child 是右孩子
                if (parent.left!=null && parent.left.color==Red){//兄弟节点为红色
                    parent.left.color = Black;
                    parent.color = Red;
                    rightRotate(parent);
                }
                //兄弟和兄弟的两个孩子都为黑色
                if ((parent.left.right!=null || parent.left.right.color==Red)&&
                        (parent.left.left==null ||parent.left.left.color==Black)){
                    parent.left.color=Red;
                    child = parent;
                }else {//兄弟黑色,兄弟左孩子红色,右孩子黑色
                    if (parent.left.left==null||parent.left.right.color==Red){
                        parent.left.right.color = Black;
                        parent.left.color = Red;
                        rightRotate(parent.left);
                    }
                    //兄弟黑色,兄弟左孩子红
                    parent.left.color = parent.color;
                    parent.color = Black;
                    if (parent.left.left!=null){
                        parent.left.left.color = Black;
                    }
                    leftRotate(parent);
                    break;
                }
            }
        }
    }
6.删除代码
 /**
     * RBT树的删除
     * @param value
     */
    public void remove(T value){
        if (this.root == null){
            return;
        }
        Entry<T> parent=null;
        Entry<T> cur = this.root;
        while (cur!=null){
            if (cur.value.compareTo(value)>0){
                parent = cur;
                cur = cur.left;
            }else if(cur.value.compareTo(value)<0){
                parent = cur;
                cur = cur.right;
            }else{
                break;//结束while循环,但是否找到,要看cur是否为空
            }
            if (cur==null){//没找到
                return;
            }
        }
        /**
         * 找到了待删value节点,cur指向待删节点
         * 先处理 待删节点有两个孩子:及就是要删除的节点有左右两个孩子
         */
        if(cur.left != null && cur.right != null){
            //找前驱节点代替待删除节点的值,然后删除前驱节点
            Entry<T> old = cur; //记录待删节点
            parent = cur;
            cur = cur.left;  //cur指向待删节点的左孩子域
            while (cur.right != null){ //找前驱结点:待删节点左子树中值最大的节点
                parent = cur;
                cur = cur.right;
            }
            //此时cur指向前驱结点
            old.value = cur.value;
        }
        /**
         * 统一删除cur指向的节点  即:有一个节点或者没有节点的情况
         * child指向待删节点的不为空的孩子,可能为null
         */
        Entry<T> child = cur.left;
        if(child == null){
            child = cur.right;
        }
        //两种情况 child为空和不为空
        if (child!=null){
            if (parent== null){//说明是根节点
                 this.root = child;
            }else if (parent.left ==cur){
                parent.left = child;
            }else {
                parent.right = child;
            }
            if (cur.color==Black && child.color == Black){//孩子和待删除节点都为黑
                removeAfter(child,parent);
            }else if (cur.color==Black && child.color == Red){//若孩子为红色,那变成黑色就好了
                child.color= Black;
            }
        }else {//孩子为空
            if (parent == null){//说明cur就是根节点,删除了就没了
                root = null;
            }else {//cur没有孩子,cur是叶子节点
                if (cur.color == Black){
                    removeAfter(cur,parent);
                }
                if (parent.left == cur){
                    parent = null;
                }else {
                    parent = null;
                }
            }
        }
    }

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值