红黑树

 

package tree.red.black;


/**
 * @author: tianhaichao
 * @date: 2022/8/29 14:27
 * @description: 1、根节点为黑色
 * 2、叶子节点为黑色空节点
 * 3、红色节点的孩子节点必须是黑色的
 * 4、每个分支上黑色节点个数都一样
 * 5、新插入的节点都是红色的(为了减少调整,增加黑色节点会因为打破了【4】而发起调整)
 */
public class RBTree {
    private static RBTreeNode root;

    /**
     * @author: tianhaichao
     * @date: 2022/9/20 15:55
     * @description: 红黑树插入节点,如果打破了红黑树的规则,需要自平衡做调整
     * 分以下几种情况:
     * 1、父亲节点为【黑色】  ————  插入红色节点后,没有打破红黑树的规则,【不需要做调整】
     * 2、父亲节点为【红色】  ————  插入节点为红色,需要做【颜色反转】,将父亲节点由红色变为黑色,为了不改变分支上black节点的个数,需要继而向上反转爷爷和祖先节点的颜色
     * a、叔叔节点为【红色】    -----  因为叔叔节点是红色,爷爷节点转变为红色后,叔叔节点必须要转变为黑色。在这个局部的子树中,两个分支上的black节点个数都没有变,所以【不需要左旋或右旋调整】。
     * b、叔叔节点为【黑色或空】 -----  因为叔叔节点是黑色,因为爷爷节点转而为红色,使得叔叔分支上的black节点少了一个,所以需要通过【自平衡】重新回到红黑树
     * (叶子结点也是黑色)
     * RL: 1、执行【颜色反转】2、先以【父节点】为轴【左旋】3、再以【爷爷节点】为轴【右旋】
     * RR: 1、执行【颜色反转】2、以【爷爷节点】为轴【右旋
     * LR: 1、执行【颜色反转】2、先以【父节点】为轴【右旋】3、再以【爷爷节点】为轴【左旋】
     * LL: 1、执行【颜色反转】2、以【爷爷节点】为轴【左旋】
     */
    public static void insert(int data) {
        RBTreeNode node = new RBTreeNode(data);
        if (root == null) {
            // 根节点是黑色的
            root = node;
            setBlack(node, true);
            return;
        } else {
            // 如果根节点不为空,循环找到叶子节点,执行树插入
            RBTreeNode parentNode = root;
            RBTreeNode sonNode = null;
            // 比较大小分边
            if (data >= parentNode.getData()) {
                // 遍历右边
                sonNode = parentNode.getRight();
            } else {
                // 遍历左边
                sonNode = parentNode.getLeft();
            }

            // 子节点不为空,继续遍历,直到找到叶子节点
            while (sonNode != null) {
                parentNode = sonNode;
                if (data >= parentNode.getData()) {
                    // 遍历右边
                    sonNode = parentNode.getRight();
                } else {
                    // 遍历左边
                    sonNode = parentNode.getLeft();
                }
            }
            // sonNode == null 是叶子节点
            if (data >= parentNode.getData()) {
                //插入右节点
                parentNode.setRight(node);
                node.setPrarent(parentNode);
                // 新建都是红色
                setBlack(node, false);
            } else {
                //插入左节点
                parentNode.setLeft(node);
                node.setPrarent(parentNode);
                // 新建都是红色
                setBlack(node, false);
            }
            // 执行自平衡平衡方法
            balanceInsert(node);

        }
    }

    public static void setBlack(RBTreeNode node, boolean isBlack) {
        if(node == null){
            return;
        }
        if (node != root) {
            node.setBlack(isBlack);
        } else {
            node.setBlack(true);
        }
    }

    /**
     * @author: tianhaichao
     * @date: 2022/9/20 17:38
     * @description:自平衡分以下几种情况: 1、父亲节点为【黑色】  ————  插入红色节点后,没有打破红黑树的规则,【不需要做调整】
     * 2、父亲节点为【红色】  ————  插入节点为红色,需要做【颜色反转】,将父亲节点由红色变为黑色,为了不改变分支上black节点的个数,需要继而向上反转爷爷和祖先节点的颜色
     * a、叔叔节点为【红色】    -----  因为叔叔节点是红色,爷爷节点转变为红色后,叔叔节点必须要转变为黑色。在这个局部的子树中,两个分支上的black节点个数都没有变,所以【不需要左旋或右旋调整】。
     * b、叔叔节点为【黑色或空】 -----  因为叔叔节点是黑色,因为爷爷节点转而为红色,使得叔叔分支上的black节点少了一个,所以需要通过【自平衡】重新回到红黑树
     * (叶子结点也是黑色)
     * RL: 1、执行【颜色反转】2、先以【父节点】为轴【左旋】3、再以【爷爷节点】为轴【右旋】
     * RR: 1、执行【颜色反转】2、以【爷爷节点】为轴【右旋
     * LR: 1、执行【颜色反转】2、先以【父节点】为轴【右旋】3、再以【爷爷节点】为轴【左旋】
     * LL: 1、执行【颜色反转】2、以【爷爷节点】为轴【左旋】
     */
    public static void balanceInsert(RBTreeNode node) {
        //判断father的颜色
        RBTreeNode farther = node.getPrarent();
        // 父节点是红色的,需要自平衡
        while (farther != root && farther != null && farther.isBlack() == false) {
            RBTreeNode grandFarther = farther.getPrarent();
            // 父亲节点是祖父的右孩子
            if (grandFarther.getRight() == farther) {
                // 判断叔叔节点颜色 —— red 【颜色反转】不需要【左旋】或【右旋】调整
                // 如果叔叔节点不为空,且为红色,反转叔叔节点颜色,祖父节点作为父节点向上传递
                if (grandFarther.getLeft() != null && grandFarther.getLeft().isBlack() == false) {
                    setBlack(farther, true);
                    setBlack(grandFarther, false);
                    setBlack(grandFarther.getLeft(), true);
                    // 此时的改变已经传递到了祖父,祖父变成了红色,相当于新插入
                    farther = grandFarther.getPrarent();
                    node= grandFarther;
                    continue;
                } else {
                    //叔叔节点为黑色或空
                    if(farther.getLeft()==node){
                        //RL 右旋 成为RR 然后 以祖父节点左旋
                        rightRotate(farther);
                    }
                    //RR
                    farther = leftRotate(grandFarther);
                    setBlack(farther, true);
                    setBlack(farther.getRight(), false);
                    setBlack(farther.getLeft(), false);
                    // 通过调整,father替代祖父的位置变成了黑色,所有分支黑色节点的个数没有变化,整棵树重新满足红黑树规则,跳出循环
                    break;
                }

            }
            // 父亲节点是祖父的左孩子
            if (grandFarther.getLeft() == farther) {
                // 判断叔叔节点颜色 —— red 【颜色反转】不需要【左旋】或【右旋】调整
                // 如果叔叔节点不为空,且为红色,反转叔叔节点颜色,祖父节点作为父节点向上传递
                if (grandFarther.getRight() != null && grandFarther.getRight().isBlack() == false) {
                    setBlack(farther, true);
                    setBlack(grandFarther, false);
                    setBlack(grandFarther.getRight(), true);
                    // 此时的改变已经传递到了祖父,祖父变成了红色,相当于新插入
                    farther = grandFarther.getPrarent();
                    node= grandFarther;
                    continue;
                } else {
                    //叔叔节点为黑色或空
                    if(farther.getRight()==node){
                        //LR 左旋 成为LL 然后 以祖父节点右旋
                        farther = leftRotate(farther);
                    }
                    //LL
                    farther = rightRotate(grandFarther);
                    setBlack(farther, true);
                    setBlack(farther.getRight(), false);
                    setBlack(farther.getLeft(), false);
                }
            }

        }

    }

    /**
     * @author: tianhaichao
     * @date: 2022/8/30 14:44
     * @description: node 旋转节点 RR 、LR执行左旋
     * 1、将右节点上提成为父节点
     * 2、父节点转为右节点的左孩子
     * 3、父节点的右孩子【替换成】右节点的左孩子,父节点的左孩子不变
     * return 旋转完后的父节点,也就是右孩子【将右节点上提成为父节点】
     */
    public static RBTreeNode leftRotate(RBTreeNode node) {
        RBTreeNode right = node.getRight();
        RBTreeNode father = node;
        // 如果右节点存在左孩子,父节点的右孩子【替换成】右节点的左孩子
        father.setRight(right.getLeft());
        // 将右节点上提成为父节点 变成祖父的孩子
        if (father.getPrarent() == null || father == root) {
            root = right;
        } else if (father.getPrarent().getLeft() == father) {
            father.getPrarent().setLeft(right);
        } else {
            // 如果旋转节点是父节点的右节点
            father.getPrarent().setRight(right);
        }
        right.setPrarent(father.getPrarent());
//        父节点转为右节点的左孩子
        right.setLeft(father);
        father.setPrarent(right);
        return right;
    }

    /**
     * @return 旋转之后的父节点,也就是左孩子【将左节点上提成为父节点】
     * @author: tianhaichao
     * @date: 2022/9/20 18:31
     * @description: LL或RL执行右旋
     * 执行右旋,
     * 1、将左节点上提成为父节点
     * 2、父节点转为左节点的右孩子
     * 3、父节点的左孩子【替换成】左节点的右孩子,父节点的右孩子不变
     */
    public static RBTreeNode rightRotate(RBTreeNode node) {
        RBTreeNode left = node.getLeft();
        RBTreeNode father = node;
        // 父节点的左孩子【替换成】左节点的右孩子,父节点的右孩子不变
        father.setLeft(left.getRight());
        //将左节点上提成为父节点
        if (father.getPrarent() == null || father == root) {
            root = left;
        } else if (father.getPrarent().getRight() == father) {

            father.getPrarent().setRight(left);
        } else {
            father.getPrarent().setLeft(left);
        }
        left.setPrarent(father.getPrarent());
        // 父节点转为左节点的右孩子
        father.setPrarent(left);
        left.setRight(father);
        return left;
    }

    public static void main(String[] args) {
        int[] array = new int[]{10, 3, 5, 2, 5, 6, 4, 12, 15, 16, 17, 18};
        for (int i : array) {
            RBTree.insert(i);
            preTraversal(root);
            System.out.println("----" + i);
        }
        System.out.println("==========================");
        //排序输出
        midTraversal(root);
    }

    /**
     * @author: tianhaichao
     * @date: 2022/9/20 15:10
     * @description:中序遍历
     */
    public static void midTraversal(RBTreeNode node) {
        if (node == null) {
            return;
        }
        midTraversal(node.getLeft());
        System.out.print(node.getData() +"  ");
        midTraversal(node.getRight());
    }
    /**
     * @author: tianhaichao
     * @date: 2022/9/20 15:10
     * @description:中序遍历
     */
    public static void preTraversal(RBTreeNode node) {
        if (node == null) {
            return;
        }
        int left = node.getLeft()!=null?node.getLeft().getData():0;
        int right = node.getRight() != null?node.getRight().getData():0;
        System.out.print(node.getData() +"【"+node.isBlack()+left+"  "+right+"】"+ "  ");
        preTraversal(node.getLeft());
        preTraversal(node.getRight());
    }
}


package tree.red.black;

/**
 * @author: tianhaichao
 * @date: 2022/8/29 14:20
 * @description: 红黑树节点
 */
public class RBTreeNode {
    private int data;
    private RBTreeNode prarent;
    private RBTreeNode left;
    private RBTreeNode right;
// get set方法省略……
}

  

 

 验证工具:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

 

posted @ 2022-09-21 17:34  田海超  阅读(30)  评论(0编辑  收藏  举报