首页 > 其他分享 >红黑树

红黑树

时间:2022-09-21 17:48:37浏览次数:65  
标签:node RBTreeNode father farther 红黑树 setBlack 节点

 

 

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
                    leftRotate(grandFarther);
                    setBlack(farther, true);
                    setBlack(farther.getRight(), false);
                    setBlack(farther.getLeft(), false);
                    farther = grandFarther;
                    node=farther;
                }

            }
            // 父亲节点是祖父的左孩子
            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);
                    farther = farther.getPrarent();
                    node = farther;
                }
            }

        }

    }

    /**
     * @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;
    private boolean isBlack;

 // set 、get方法省略………………
}

  

 

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

 

标签:node,RBTreeNode,father,farther,红黑树,setBlack,节点
From: https://www.cnblogs.com/tianhaichao/p/16716461.html

相关文章

  • 【数据结构】红黑树与平衡二叉树的区别以及原理详解(附图解)
    引用网址:https://blog.csdn.net/weixin_44780082/article/details/112239269文章目录前言一、什么是红黑树1.1平衡二叉树1.2红黑树1.3平衡二叉树和红黑树的区别二、红黑......
  • 判断红黑树
    https://www.acwing.com/problem/content/1630/思路:思路不难,按照题目意思判断即可,但是这个dfs有些难写,值得学习,特记录此题。#include<iostream>#include<algorithm>......
  • 红黑树:基于平衡搜索树的效率改进
    出现背景平衡二叉树解决了搜索二叉树可能过长导致查找次数过多的问题,但是随着不断的插入和删除,平衡二叉树算法需要不断的旋转以达到最佳结构,这个旋转操作浪费了不少时间,平......
  • 红黑树以及JAVA实现(一)
    目录前言一、B树1.1概念1.22-3-4树1.32-3-4树的插入节点分类1.42-3-4树的删除1.4.1当删除节点是叶子节点1.4.1.1当删除节点为非2节点1.4.1.2当删除节点为2节点1.4.......
  • 红黑树以及JAVA实现(二)
    目录红黑树的删除1.删除节点为叶子节点1.1删除节点为红色节点1.2删除节点为黑色1.2.1要删除的节点D是黑色,D的兄弟节点B也是黑色没有侄子节点1.2.2要删除的节点D是黑色......