红黑树的insert插入操作--Java实现

其实红黑树插入元素的过程和BST树没什么区别,主要是插入之后复原树的过程,要分三种情况对待。

情况一:要插入节点(D)的父节点(B)为红色,叔叔节点(C)为黑色,并且要插入的节点(D)和父节点(B)同一侧

这种情况,就要将待插入节点的父节点(B)和祖先节点(A)颜色互换,再以祖先节点(A)为基准进行右旋转或左旋转(如果父节点在祖先节点的左侧,那么进行右旋转;如果父节点在祖先节点的右侧,进行左旋转)

上图的情况,显然需要以祖先节点(A)为基准右旋转,一系列复原操作后的树将会是如下模样:

--------------------------------------------------------------------------------------------------------------------------------------

情况二:要插入节点(D)的父节点(B)为红色,叔叔节点(C)为黑色,并且要插入的节点(D)和父节点(B)不在同一侧

这种情况,首先要以父亲节点(B)为基准进行左旋转或右旋转(保证待插入节点(D)和其父亲节点(B)在同一侧即可),上图显然要进行左旋转。

之后,重复情况1中的操作即可。

复原操作后的树将会是如下模样:

--------------------------------------------------------------------------------------------------------------------------------------

情况三:待插入节点(D)的父亲节点(B)和叔叔节点(C)都是红色。

这种情况,首先把父亲节点(B)和叔叔节点(C)都变成黑色,再把祖先节点(A)变成红色。

再以祖先节点(A)当前节点cur向根节点遍历判断,直到当前节点cur为树根时,将当前节点染黑即可。

这种情况的复原树操作后,树将会是这样的模样:

--------------------------------------------------------------------------------------------------------------------------------------

此处应该有代码:

我就把包括节点类型的定义,左旋转和右旋转的实现一并发出来吧,方便大家自己测试。

左旋转和右旋转如果不太了解的话,之前我有写过一片关于红黑树左右旋转的具体操作:

https://blog.csdn.net/weixin_43729854/article/details/104579104

以下代码结合上述我分析的三种情况看,应该不难理解。

注意情况二和情况一的一并处理,需要在情况二进行第一部分操作后,将当前节点node重新指向离根节点最远的那个节点。

/**
 * 定义枚举类型的节点颜色
 */
enum Color {
    RED,
    BLACK
}

/**
 * 红黑树的实现
 *
 * @param <T>
 */
class RBTree<T extends Comparable<T>> {
    /**
     * 指向红黑树的根节点
     */
    private Entry<T> root;

    public RBTree() {
        this.root = null;
    }

    /**
     * 红黑树的插入操作
     * @param val
     */
    public void insert(T val){
        //根节点是黑色
        if(this.root==null){
            this.root= new Entry<>(val,Color.BLACK);
            return;
        }

        //搜索合适位置插入val
        Entry<T> parent=null;
        Entry<T> cur = this.root;
        while(cur!=null){
            parent = cur;
            if(cur.data.compareTo(val)>0){
                cur=cur.left;
            }else if(cur.data.compareTo(val)<0){
                cur=cur.right;
            }else {
                return;
            }
        }

        //找到位置后插入node节点
       Entry<T> node = new Entry<>(val,Color.RED);
        //更新插入节点的parent
        node.parent=parent;
        if(parent.data.compareTo(val)>0){
            parent.left=node;
        }else {
            parent.right=node;
        }

        //如果插入节点的父节点也是红色,破坏了红黑树性质,需要进行复原树操作
        if(parent.color==Color.RED){
            fixAfterInsert(node);
        }
    }

    /**
     * 红黑树的插入后复原树操作
     * @param node
     */
    private void fixAfterInsert(Entry<T> node) {
        //判断待插入节点的父节点是否也是红色节点,如果是,进入循环来复原树。
        while (color(parent(node))==Color.RED){
            //如果插入节点在祖先节点的左侧
            if(left(parent(parent(node)))==parent(node)){
                //定义叔叔节点
                Entry<T> uncle = right(parent(parent(node)));
                //如果叔叔节点为红色
                //图中情况三
                if(color(uncle)==Color.RED){
                    //把父亲和叔叔节点都变为黑色
                    setColor(parent(node),Color.BLACK);
                    setColor(uncle,Color.BLACK);
                    //把祖先节点变为红色
                    setColor(parent(parent(node)),Color.RED);
                    //node指向祖先节点,继续向根节点遍历,检查是否还需要调整
                    node=parent(parent(node));
                }else {
                    //处理图中 情况二 的调整在同一侧的部分
                    // 因为剩下的旋转部分和图中 情况一 的操作相同,所以之后一并处理
                    if(right(parent(node))==node){
                        //这一步是为了在 调整在同一侧 操作后,让node指向最远离根节点的那个节点
                        //换言之就是 让node的指向状态,和 情况一 中的指向状态相同,方便一并处理
                        node = parent(node);
                        leftRotate(node);
                    }
                    //处理图中 情况一
                    setColor(parent(node),Color.BLACK);
                    setColor(parent(parent(node)),Color.RED);
                    rightRotate(parent(parent(node)));
                    break;
                }
            }else {
                //如果插入节点在祖先节点的右侧

                //定义叔叔节点
                Entry<T> uncle = left(parent(parent(node)));
                //如果叔叔节点为红色
                //图中情况三
                if (color(uncle) == Color.RED) {
                    //把父亲和叔叔节点都变为黑色
                    setColor(parent(node), Color.BLACK);
                    setColor(uncle, Color.BLACK);
                    //把祖先节点变为红色
                    setColor(parent(parent(node)), Color.RED);
                    //node指向祖先节点,继续向根节点遍历,检查是否还需要调整
                    node = parent(parent(node));
                } else {
                    //处理图中 情况二 的调整在同一侧的部分
                    // 因为剩下的旋转部分和图中 情况一 的操作相同,所以之后一并处理
                    if (left(parent(node)) == node) {
                        //这一步是为了在 调整在同一侧 操作后,让node指向最远离根节点的那个节点
                        //换言之就是 让node的指向状态,和 情况一 中的指向状态相同,方便一并处理
                        node = parent(node);
                        rightRotate(node);
                    }
                    //处理图中 情况一
                    setColor(parent(node), Color.BLACK);
                    setColor(parent(parent(node)), Color.RED);
                    leftRotate(parent(parent(node)));
                    break;
                }
            }
        }
    }


    /**
     *封装一些接口,方便实现复原树操作的编码
     */
    private Entry<T> parent(Entry<T> node){
        return  node.parent;
    }
    private Entry<T> left(Entry<T> node){
        return node.left;
    }
    private Entry<T> right(Entry<T> node){
        return node.right;
    }
    private Color color(Entry<T> node){
        return node.color;
    }
    private void setColor(Entry<T> node,Color color){
        node.color=color;
    }



    /**
     * 节点的左旋转操作
     *
     * @param node
     */
    private void leftRotate(Entry<T> node) {
        Entry<T> child = node.right;
        child.parent = node.parent;
        //判断node节点是否为根节点,如果是,那么child将成为根节点
        if (null == node.parent) {
            this.root = child;
        } else {
            //判断node节点是它父亲节点的左孩子还是右孩子,再进行移动
            if (node.parent.left == node) {
                node.parent.left = child;
            } else {
                node.parent.right = child;
            }
        }//以上为①操作


        node.right = child.left;
        if (null != child.left) {
            child.left.parent = node;
        }
        //以上为②操作
        child.left = node;
        node.parent = child;
        //以上为③操作
    }

    /**
     * 节点的右旋转操作
     *
     * @param node
     */
    private void rightRotate(Entry<T> node) {
        Entry<T> child = node.left;
        child.parent = node.parent;
        if (null == node.parent) {
            this.root = child;
        } else {
            if (node.parent.left == node) {
                node.parent.left = child;
            } else {
                node.parent.right = child;
            }
        }
        node.left = child.right;
        if (null != child.right) {
            child.right.parent = node;
        }
        child.right = node;
        node.parent = child;
    }

    /**
     * 节点类型定义
     *
     * @param <T>
     */
    private static class Entry<T extends Comparable<T>> {
        T data;
        Entry<T> left;
        Entry<T> right;
        Entry<T> parent;
        Color color;

        public Entry(T data, Entry<T> left, Entry<T> right, Entry<T> parent, Color color) {
            this.data = data;
            this.left = left;
            this.right = right;
            this.parent = parent;
            this.color = color;
        }

        public Entry(T data, Color color) {
            this.data = data;
            this.color = color;
        }

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值