java实现红黑树 新增节点 左旋右旋 hash取模

本文介绍了红黑树的基本结构,包括节点定义、颜色属性、辅助方法(parentOf、isRed、isBlack、setRed和inOrderPrint),以及插入操作的实现,重点讲解了修复红黑树失衡的详细步骤。
摘要由CSDN通过智能技术生成
package com.yeyu.study1.list;

/**
 * 红黑树
 *
 *  1、构建节点对象结构 定义颜色属性
 *  2、辅助方法定义:  parentOf(node) isRed(node) , isBlack(node), setRed(node) , inOrderPrint()
 *  3、左旋方法
 *  4、右旋方法
 *  5、定义公开的插入方法
 *  6、实现内部的插入方法
 *  7、实现修正插入导致红黑树失衡的方法定义:
 *
 *
 * key继承 Comparable用于给K排序
 */
public class RbTree<K extends Comparable,V>{
    private static final boolean RED = true;  //红色
    private static final boolean BLACK = false; //黑色

    /** 根节点 */
    private RBNode root;

    public RBNode getRoot(){
        return root;
    }
    /**
     * 获得当前节点的父节点
     * @param node
     */
    private RBNode parentOf(RBNode node){
        if(node!=null){
            return node.parent;
        }
        return null;
    }

    /**
     * 节点是否为红色
     * @param node
     * @return
     */
    private boolean isRed(RBNode node){
        if(node!=null){
            return node.color == RED;
        }
        return false;
    }

    /**
     * 节点是否为黑色
     * @param node
     * @return
     */
    private boolean isBlack(RBNode node){
        if(node!=null){
            return node.color == BLACK;
        }
        return false;
    }

    /**
     * 设置节点为红色
     * @param node
     */
    private void setRed(RBNode node){
        if(node!=null){
            node.color = RED;
        }
    }
    /**
     * 设置节点为黑色
     * @param node
     */
    private void setBlack(RBNode node){
        if(node!=null){
            node.color = BLACK;
        }
    }

    /**
     * 中序打印二叉树
     */
    public void inOrderPrint(){
        inOrderPrint(root);
    }
    private void inOrderPrint(RBNode node){
        if(node != null){
            inOrderPrint(node.left);
            System.out.println("key:"+node.k + ", value:"+node.v);
            inOrderPrint(node.right);
        }
    }


    /**
     * 公开的插入方法
     * @param key
     * @param value
     */
    public void put(K key,V value){
        RBNode node = new RBNode();
        node.setColor(RED);//新节点都是红色
        node.setK(hash(key));
        node.setV(value);
        put(node);
    }


    /**
     * 参考hashmap 给key做取模运算  可以插入key 为 null
     * @param key
     * @return
     */
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    /**
     * 内部的插入方法
     * @param node
     */
    private void put(RBNode node){
        //1、查找当前节点的父节点
        RBNode parent = null;
        RBNode x = this.root;
        while (x != null){
            parent = x;
            //排序方法 返回 >0 说明 node.k 大于x.k 需要到x的右子树查找
            //排序方法 返回 =0 说明 node.k 等于x.k 进行value的替换
            //排序方法 返回 <0 说明 node.k 小于x.k 需要到x的左子树查找
            int flag = node.k.compareTo(x.k);
            if(flag>0){
                x = x.right;
            }else if(flag==0){
                x.setV(node.getV());
                return;
            }else {
                x = x.left;
            }
        }
        node.parent = parent;
        //判断node与parent的key 谁大
        if(parent!=null){
            int flag = node.k.compareTo(parent.k);
            if(flag>0){ //当前node.k比parent的k大,需要把node放入parent的右节点
                parent.right = node;
            }else{  //当前node.k比parent的k小,需要把node放入parent的左节点
                parent.left = node;
            }
        }else {
            this.root = node;
        }

        //需要调用修复红黑树平衡方法 检测本次插入是否破坏平衡
        putFixUp(node);
    }

    /**
     * 修复红黑树方法:
     * 1、如 红黑树为空树  将根节点变黑
     * 2、如 插入的节点已存在  不需要处理 直接替换V即可
     * 3、如 插入的父节点为黑色 , 不需要处理  因为黑色节点没有加深 所以红黑树依旧平衡
     *
     *
     * 4、如  插入节点的父节点为红色  需要处理  不能红红相连
     *      4.1 叔叔节点存在并且为红色 (父-叔 双红)  处理方式:将爸爸叔叔变为黑色,将爷爷染为红色  并且再以爷爷为当前节点进行下一轮处理
     *      4.2 叔叔节点不存在  或者为黑色  父节点是爷爷节点的左子树
     *          4.2.1   插入节点为其父节点的左子节点(LL 左左) 将爸爸变为黑色,将爷爷变为红色 然后以爷爷节点右旋,就完成了
     *          4.2.2   插入节点为其父节点的右子节点(LR 左右) 以爸爸节点进行左旋,得到LL双红(4.2.1)的情景 然后指定爸爸节点进行下一轮处理
     *      4.3 叔叔节点不存在  或者为黑色  父节点是爷爷节点的右子树
     *          4.3.1   插入节点为其父节点的右子节点(RR情况) 将爸爸变为黑色,将爷爷变为红色 然后以爷爷节点进行左旋 就可以了
     *          4.3.2   插入节点为其父节点的左子节点(RL情况) 以爸爸节点进行右旋  得到RR双红情景(4.3.1) 然后指定爸爸节点进行下一轮处理
     *
     */
    private void putFixUp(RBNode node){
        this.root.setColor(BLACK);  //解决1场景

        RBNode parent = parentOf(node);  //拿到爸爸节点

        RBNode gparent = parentOf(parent);  //爷爷节点

        //4场景: 插入节点的父节点为红色
        if(parent != null && isRed(parent)){
            //如果父节点是红色 那么一定存在爷爷节点  因为跟根节点不可能是红色

            RBNode uncle = null; //叔叔节点

            if(parent == gparent.left){ //判断父节点是否是爷爷的左节点
                uncle = gparent.right;
                //4.1情景:叔叔节点存在并且为红色 (父-叔 双红)  处理方式:将爸爸叔叔变为黑色,将爷爷染为红色  并且再以爷爷为当前节点进行下一轮处理
                if(uncle!=null && isRed(uncle)){
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(gparent);
                    inOrderPrint(gparent);
                    return;
                }
                //4.2场景:叔叔节点不存在或者为黑色
                if(uncle == null || isBlack(uncle)){
                    //4.2.1   插入节点为其父节点的左子节点(LL 左左) 将爸爸变为黑色,将爷爷变为红色 然后以爷爷节点右旋,就完成了
                     if(node==parent.left){
                         setBlack(parent);
                         setRed(gparent);
                         rightRotate(gparent);
                         return;
                     }

                    //4.2.2   插入节点为其父节点的右子节点(LR 左右)
                    // 以爸爸节点进行左旋,得到LL双红(4.2.1)的情景 然后指定爸爸节点进行下一轮处理
                    if(node==parent.right){
                        leftRotate(parent);
                        putFixUp(parent);
                        return;
                    }

                }


            }else{ //父节点为爷爷的右节点
                uncle = gparent.left;
                //4.1情景:叔叔节点存在并且为红色 (父-叔 双红)  处理方式:将爸爸叔叔变为黑色,将爷爷染为红色  并且再以爷爷为当前节点进行下一轮处理
                if(uncle!=null && isRed(uncle)){
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(gparent);
                    putFixUp(gparent);
                    return;
                }

                //4.3 叔叔节点不存在  或者为黑色  父节点是爷爷节点的右子树
                if(uncle==null || isBlack(uncle)){
                    //4.3.1   插入节点为其父节点的右子节点(RR情况) 将爸爸变为黑色,将爷爷变为红色 然后以爷爷节点进行左旋 就可以了
                    if(node==parent.right){
                        setBlack(parent);
                        setRed(gparent);
                        leftRotate(gparent);
                        return;
                    }
                    //4.3.2   插入节点为其父节点的左子节点(RL情况) 以爸爸节点进行右旋  得到RR双红情景(4.3.1) 然后指定爸爸节点进行下一轮处理
                    if(node==parent.left){
                        rightRotate(parent);
                        putFixUp(parent);
                        return;
                    }
                }


            }
        }

    }

    /**
     * 左旋方法
     * 1、将右子节点的左子节点改为自身的右节点 并将其的父节点改为自身
     * 2、当自身父节点不为空时,更新原右子节点的父节点为原父节点  并将父节点的字数指向原右子节点 并将自身的父节点只想原右节点
     */
    private void leftRotate(RBNode x){
        RBNode y  = x.right;  //拿到右子节点
        x.right = y.left;
        if(y.left != null){
            y.left.parent = x;
        }
        if(x.parent!=null){
            y.parent = x.parent;

            if(x == x.parent.left){
                x.parent.left = y;
            }else {
                x.parent.right = y;
            }
        }else {
            this.root = y;
            this.root.parent=null;
        }
        //更新父节点
        x.parent = y;
        y.left = x;
    }


    /**
     * 右旋方法
     * 和左旋相反的操作
     * @param x
     */
    private void rightRotate(RBNode x){
        RBNode y = x.left;
        x.left = y.right;
        if(y.right!=null){
            y.right.parent=x;
        }
        if(x.parent!=null){
            y.parent = x.parent;

            if(x == x.parent.left){
                x.parent.left = y;
            }else {
                x.parent.right = y;
            }
        }else {
            this.root = y;
            this.root.parent=null;
        }
        x.parent=y;
        y.right = x;
    }

    public static class RBNode<K extends Comparable,V>{
        private RBNode parent; //父节点
        private RBNode left; //左节点
        private RBNode right; //右节点
        private boolean color; //颜色  红黑
        private K k;
        private V v;


        public RBNode() {
        }

        public RBNode getParent() {
            return parent;
        }

        public void setParent(RBNode parent) {
            this.parent = parent;
        }

        public RBNode getLeft() {
            return left;
        }

        public void setLeft(RBNode left) {
            this.left = left;
        }

        public RBNode getRight() {
            return right;
        }

        public void setRight(RBNode right) {
            this.right = right;
        }

        public boolean isColor() {
            return color;
        }

        public void setColor(boolean color) {
            this.color = color;
        }

        public K getK() {
            return k;
        }

        public void setK(K k) {
            this.k = k;
        }

        public V getV() {
            return v;
        }

        public void setV(V v) {
            this.v = v;
        }

        public RBNode(RBNode parent, RBNode left, RBNode right, boolean color, K k, V v) {
            this.parent = parent;
            this.left = left;
            this.right = right;
            this.color = color;
            this.k = k;
            this.v = v;
        }

    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值