Java 数据结构篇-实现红黑树的核心方法

这篇具有很好参考价值的文章主要介绍了Java 数据结构篇-实现红黑树的核心方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
  

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

 

文章目录

        1.0 红黑树的说明

        2.0 红黑树的特性

        3.0 红黑树的成员变量及其构造方法

        4.0 实现红黑树的核心方法

        4.1 红黑树内部类的核心方法

        (1)判断当前节点是否为左孩子节点 - isLeftChild()

        (2)获取叔叔节点 - uncle()

        (3)获取兄弟节点 - brother()

        4.2 红黑树外部类的核心方法

        (1)判断是否为红色节点 isRed - (TreeNode node)

        (2)判断是否为黑色节点 isBlack - (TreeNode node)

        (3)右旋 - rightRotate(TreeNode node)

        (4)左旋 - leftRotate(TreeNode node)

        (5)更新、添加节点 - put(int key,Object value)

        (6)删除节点 - remove(int key)

        5.0 实现红黑树核心方法的完整代码


        1.0 红黑树的说明

        红黑树是一种自平衡的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是红色或黑色。

        与 AVL 树相比之下,红黑树放宽了对平衡的要求,通过牺牲一定的平衡性能来换取更高的插入、删除和查找操作的性能。红黑树的旋转操作相对较少,因此在实际应用中,红黑树更常用于需要高效的动态数据结构,如集合、映射等。而 AVL 树则更适用于对平衡性要求较高的场景,如数据库索引等。

        总的来说,红黑树也是一种自平衡的二叉搜索树,较之 AVL 树,插入和删除时旋转次数更少。

        2.0 红黑树的特性

        - 所有节点都有两种颜色:红与黑

        - 所有 null 视为黑色

        - 红色节点不能相邻

        - 根节点是黑色

        - 从根节点到任意一个叶子节点,路径中的黑色节点数一样(黑色完美平衡)

        前四个规则都很容易理解,接下来详细说明一下最后一个规则,到底什么是从根节点到叶子节点的所有路径都要保证黑色节点数一样?

如图 1:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        从根节点出发到叶子节点,首先从左子树方向开始,6 -> 2 -> 1 -> null ,这一条路径的黑色节点一共有 3 个;现在从左子树方向出发,6 -> 2 -> null ,这一条路径的黑色节点一共有 3 个;现在从右子树方向开始,6 -> 8 -> null ,这一条路径的黑色节点一共也是有 3 个黑色节点;这几条路径的黑色节点都为 3 。因此满足红黑树的最后一条规则。

如图 2:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        同理,从根节点出发到叶子节点,先从左子树方向开始,6 -> 2 -> 1 -> null ,这一条路径的黑色节点一共有三个;再从 6 -> 2 -> null ,但是这一条路径的黑色节点只有 2 个黑色节点,不满足红黑树的最后一个规则。

        在构建红黑树需要满足以上规则,无论插入、删除等都要满足红黑树的特性。

        3.0 红黑树的成员变量及其构造方法

        节点类 TreeNode 作为内部类,该内部类的成员变量有:

        int key : 关键字,用于比较大小

        Object value : 值

        TreeNode left : 左节点

        TreeNode right : 右节点

        Color color :颜色,默认设置为红色

        TreeNode parent :该节点的父亲节点

        该内部类的构造方法:

        节点类的构造方法主要是参数为 (int key, Object value) 的构造方法

        红黑树的外部类的成员变量主要为:

        TreeNode root :根节点,一般默认为 null  

        红黑树的外部类的构造方法:

        主要采取默认的参数为 null 的构造方法

代码如下:

import static TreeNode.RedBlackTree.Color.BLACK;
import static TreeNode.RedBlackTree.Color.RED;

public class RedBlackTree {

    enum Color {
        RED,BLACK;
    }

    private TreeNode root;

    private static class TreeNode {
        int key;
        Object value;
        TreeNode left;
        TreeNode right;
        TreeNode parent;
        Color color = RED;

        //构造方法
        public TreeNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

    }

        4.0 实现红黑树的核心方法

        为了更好的实现插入、删除等方法,需要先实现基础的方法 "打好基础"。主要分为实现内部类与外部类的核心方法。

        4.1 红黑树内部类的核心方法

        (1)判断当前节点是否为左孩子节点 - isLeftChild()

                实现思路为:先判断该父亲节点是否为 null ,若 parent == null 时,则当前节点为根节点;若 parent != null ,则需要继续判断 this == parent.left ,若满足,则说明当前节点为左孩子节点,若不满足,则说明当前节点为右孩子节点。

代码如下:

        //判断是否为左孩子
         public boolean isLeftChild() {
             return parent != null && parent.left == this;
         }

        (2)获取叔叔节点 - uncle()

                叔叔节点即为跟父亲节点的同一辈的节点,跟父亲节点的父亲是同一个父亲。

                实现思路为:需要先判断该爷爷节点是否为 null 与 父亲节点是否为 null ,因为该两种情况都不具备叔叔节点。若以上情况都不为 null 时,接着还需要判断当前节点的父亲节点的位置,若父亲节点为左孩子,则叔叔节点为右孩子;若父亲节点为右孩子,则叔叔节点为左孩子。

代码如下:

         //获取叔叔节点
         public TreeNode uncle() {
             if (this.parent == null || this.parent.parent == null) {
                 return null;
             }
             if (this.isLeftChild()) {
                 return this.parent.parent.right;
             } else {
                 return this.parent.parent.left;
             }
         }

        (3)获取兄弟节点 - brother()

                跟当前节点是同一辈的节点,同一个父亲节点。

                实现思路为:先判断当前节点的父亲节点是否为 null ,若 parent == null 时,说明该节点不存在兄弟节点;若 parent != null 时,说明该节点存在兄弟节点,然后继续判断当前节点的位置,若当前节点为左孩子,则兄弟节点为:parent.right ;若当前节点为右孩子,则兄弟节点为:parent.left 。

代码如下:

         //获取兄弟节点
         public TreeNode brother() {
             if (this.parent == null) {
                 return null;
             }
             if (this.isLeftChild()) {
                 return this.parent.right;
             }else {
                 return this.parent.left;
             }
         }

        4.2 红黑树外部类的核心方法

        (1)判断是否为红色节点 isRed - (TreeNode node)

                实现思路为:根据红黑的规则可以知道,除了根节点与 null 之外, 当前节点的 color == RED 时,则该节点为红色节点。

代码如下:

    //判断是否为红色节点
    private boolean isRed(TreeNode node) {
        return node != null && node.color == RED;
    }

        (2)判断是否为黑色节点 isBlack - (TreeNode node)

                实现思路为:有两种情况下: null 或者 color == BLACK 的节点为黑色节点。

代码如下

    //判断是否为黑色节点
    private  boolean isBlack(TreeNode node) {
        return node == null || node.color == BLACK;
    }

           (3)右旋 - rightRotate(TreeNode node)

                实现思路为:跟 AVL 树的右旋会有一定的区别,因为新添加了 parent 成员,所以正常右旋完之后,需要维护 parent 变量。

                1. parent 的处理 2. 旋转后新根的父子关系

如图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        现需要将节点 8 进行右旋,假设节点 8 为 node ,那么先记录节点5、节点6,nodeLeft = node.left、nodeLeftRight = nodeLeft.right 。接着更新节点 5 的右孩子,将其更新为节点 8 作为右孩子。还需要更新节点 8 的左孩子 ,将其更新为节点 6 作为左孩子。从表面上已经完成右旋,但还需要维护节点的父亲节点 parent 。对于节点 6 来说,之前的父亲节点为节点 5 ,现在需要更新父亲节点为节点8,不过需要先判断节点 6 是否为 null ,若节点 6 为 null ,则不需要更新父亲节点的操作。若节点 6 不为 null,则需要更新父亲节点这一操作。对于节点 5 来说,节点 5 的父亲节点之前为节点 8,先父亲节点更新为节点 8 的父亲节点,因此还需要记录节点 8 的父亲节点。对于节点 8 来说,将其父亲节点更新为节点 5 。最后,由于现在的节点与节点之间时双互的,所以还需要维护新根的父子关系,不过需要判断节点 8 的父亲节点是否为 null ,若为 parent == null 时,则说明节点 5 为根节点,因此需要进行 root = node.left 调整;若 parent != null ,需要判断 node 的位置,若 node 是左孩子,那么 parent.left = node.left 进行链接;若 node 是右孩子,那么 parent.right = node.left 进行链接。那么现在就可以根据这个逻辑写代码了。

最后调整完之后的图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

代码如下:

    //右旋
    //1.考虑旋转后节点的维护parent 2.重新与上一个节点建立联系
    private void rightRotate(TreeNode node) {
        TreeNode parent = node.parent;
        TreeNode nodeLeft = node.left;
        TreeNode nodeLeftRight = nodeLeft.right;
        if (nodeLeftRight != null) {
            nodeLeftRight.parent = node;
        }
        nodeLeft.right = node;
        nodeLeft.parent = parent;
        node.left = nodeLeftRight;
        node.parent = nodeLeft;
        if (parent == null) {
            root = nodeLeft;
        } else if (parent.left == node) {
            parent.left = nodeLeft;
        }else {
            parent.right = nodeLeft;
        }

    }

        (4)左旋 - leftRotate(TreeNode node)

                跟右旋的逻辑是一摸一样的,这里就不多赘述了。

代码如下:

    //左旋
    //1.考虑旋转后节点的维护parent 2.重新与上一个节点建立联系
    private void leftRotate(TreeNode node) {
        TreeNode parent = node.parent;
        TreeNode nodeRight = node.right;
        TreeNode nodeRightLeft = nodeRight.left;
        if (nodeRightLeft != null) {
            nodeRightLeft.parent = node;
        }
        nodeRight.left = node;
        nodeRight.parent = parent;
        node.right = nodeRightLeft;
        node.parent = nodeRight;
        //2.重新与上一个节点建立联系
        if (parent == null) {
            root = nodeRight;
        } else if (parent.left == node) {
            parent.left = nodeRight;
        }else {
            parent.right = nodeRight;
        }

    }

        (5)更新、添加节点 - put(int key,Object value)

                实现思路为:正常删除节点、更新节点,若遇到红红节点不平衡,则需要进行调整

                对于正常删除、更新节点的逻辑为:根据 key 来寻找需要更新或者删除的节点、若找到了 key 的节点,那么直接更新该节点的 value 即可;若没有找到 key 的节点,需要进行添加节点。需要用到 parent 变量,记录每一次的当前节点,一旦当前节点为 node == null 时,找到了相应的空位,接着判断 key 与 parent.key 的大小。若 parent.key > key ,则 parent.left = node;若 parent.key < key 则 parent.right = node;若 parent == null 时,说明该红黑树为空树,所以 root = node 。最后对于更新来说,不会改变红黑树的平衡关系,对于添加完节点,很有可能会改变红黑树的平衡关系,所以需要进行红红修复。

                接下来详细聊一下红红节点不平衡后进行的调整:

                插入节点均视为红色

                - case 1:插入节点为根节点,将根节点变为黑色

                - case 2:插入节点的父亲若为黑色,树的红黑性质不变,无需调整

                插入节点的父亲节点为红色,触发红红相邻

                - case3:叔叔为红色

                将父亲节点变为黑色,为了保证黑色平衡,连带的叔叔也变为黑色,祖父如果是黑色不变色,会造成这颗子树黑色过多,因此祖父节点变为红色。但是祖父变为红色,可能也会继续触发红红相邻,因此对讲祖父进行递归调整。

如图 :对于 case 3 详细说明

                

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        插入节点为节点 1 设为 node ,该父亲节点为节点 2 设为 parent 。对于 node 节点来说,遇到了红红不平衡的情况,且该节点 4 即为叔叔节点为红色。

        调整红黑树平衡:需要将父亲节点、叔叔节点的颜色都要置为黑色,parent.color = BLACK,uncle.color = BALCK ,将节点 3 即祖父节点的颜色置为红色,parent.parent.color = RED 。可以发现节点 3 与节点 5 又再一次出现了触发了红红相邻了,那么利用递归来解决这一情况,一旦遇到 node == root 时,即可停止递归了。

最后调整后之后的图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

                - case 4:叔叔为黑色

                1. 父亲为左孩子,插入节点也是左孩子,此时即 LL 不平衡

                2. 父亲为左孩子,插入节点是右孩子,此时即 LR 不平衡

                3. 父亲为右孩子,插入节点也是右孩子,此时即 RR 不平衡

                4. 父亲为右孩子,插入节点是左孩子,此时即 RL 不平衡

如图:对于 case 4 中  LL 不平衡的详细说明

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        

                插入节点为节点 2 设为 node,父亲节点为节点 3 设为 parent ,该叔叔节点为 null 该节点为黑色,又满足 node 为左孩子,parent 也为左孩子, LL 不平衡。该调整需要将 parent.color = BLACK,parent.parent.color = RED,把爷爷节点 rightRotate(parent.parent) 右旋一次即可。

调整完之后的图为:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

如图:对于 case 4 中 LR 不平衡的详细说明

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

               插入节点为节点 4 设为 node,父亲节点为节点 3 设为 parent,node 的叔叔节点为 null 所以该颜色为黑色。又 parent 是左孩子,node 是右孩子,满足 LR 不平衡。

需要做的第一步,将 LR 转变为 LL ,只需要将 node 节点左旋。

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        接着,将 node.color = BLACK,parent.parent.color = RED 处理,再将爷爷节点右旋,rightRotate(parent.parent) 即可。

调整完之后的图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

         其余的 case 4 的情况大致与以上的情况相同,所以这里不再过多赘述了。  

代码如下:

    //更新、增添节点
    //正常更新、删除,遇到红红不平衡则需要进行调整
    public void put (int key, Object value) {
        TreeNode p = root;
        TreeNode parent = null;
        while (p != null) {
            parent = p;
            if (p.key > key) {
                p = p.left;
            }else if (p.key < key) {
                p = p.right;
            }else {
                p.value = value;
                return;
            }
        }
        TreeNode node = new TreeNode(key,value);
        if (parent == null) {
            root = node;
        }else {
            if (node.key > parent.key) {
                parent.right = node;

            }else {
                parent.left = node;

            }
            node.parent = parent;
        }
        //可能会发生红红不平衡,则需要调整
        fixRedRed(node);

    }
    //调整红红不平衡

    private void fixRedRed(TreeNode node) {
        //case1: 插入节点为根节点,将根节点变黑
        if(node == root) {
            node.color = BLACK;
            return;
        }
        if (isBlack(node.parent)) {
            //case2:插入节点的父亲若为黑,树的红黑性质不变,无需调整
            //无需调整
            return;
        }
        // 插入节点的父亲为红色,触发红红相邻
        //case3:叔叔为红色
        TreeNode parent = node.parent;
        TreeNode grandparent = parent.parent;
        TreeNode uncle = node.uncle();
        if (isRed(uncle)) {
            //进行变色处理即可
            //将其父亲、叔叔变为黑色,爷爷变为红色
            //若爷爷触发了红红,则继续递归调用该函数
            parent.color = BLACK;
            uncle.color = BLACK;
            grandparent.color = RED;
            fixRedRed(grandparent);
            return;
        }
        
        //case4:叔叔为黑色
        //该父亲为左孩子,该插入点也为左孩子,则触发 ll
        if (parent.isLeftChild() && node.isLeftChild()) {
            //先将父亲变为黑色、爷爷变为红色,再右旋转
            parent.color = BLACK;
            grandparent.color = RED;
            rightRotate(grandparent);
        }else if (parent.isLeftChild()) {
            //该插入节点为右孩子、该父亲为左孩子,则触发 lr
            //先左旋变为 ll 情况
            leftRotate(parent);
            node.color = BLACK;
            grandparent.color = RED;
            rightRotate(grandparent);
        } else if (!node.isLeftChild()) {
            //插入节点为右孩子、父亲节点也为右孩子 rr
            parent.color = BLACK;
            grandparent.color = RED;
            leftRotate(grandparent);
        }else {
            //插入节点为左孩子、父亲节点为右孩子 rl
            rightRotate(parent);
            node.color = BLACK;
            grandparent.color = RED;
            leftRotate(grandparent);

        }
    }

        (6)删除节点 - remove(int key)

                实现思路:正常删除节点,若遇到黑黑不平衡,则需要进行调整

                正常删除节点的逻辑:先找到需要删除的节点,为了后续的代码简洁,因此单独实现寻找删除节点的方法,寻找代码的逻辑这里就不过多展开了。又单独实现寻找删除节点的替代节点的方法,这个方法的简单逻辑为:若删除节点没有左右孩子,则替代的节点为 null ;若删除节点只有一个孩子,则返回不为 null 的孩子;若删除节点有两个节点,则找到右子树的最小节点返回即可。

        现在找到了删除节点,判断该删除节点是否为 null ,若 delete == null ,则说明没有找到删除节点,结束删除过程;若 delete != null ,则说明可以找到删除节点,则继续通过根据删除节点查找替换节点的方法找到替换节点,最后就可以删除节点了。对于删除节点是一个复杂的操作,所以单独为一个方法 doRemove(TreeNode deleted) 。

        对于实现 doRemove(TreeNode deleted) 方法,需要考虑以下情况:

         - case0: 对于删除节点有两个孩子来说,需要将其转换为只有一个孩子或者没有孩子情况。这里用到了李代桃僵技巧,例如,需要删除节点 deleted ,替代节点 replace ,将该两个节点的关键字 key 与值 value 交换,那么现在需要删除的节点不再是 deleted 了,而是 replace 。递归调用 doRemove(replace) 。这样子就可以将两个孩子的情况转变为一个孩子或者没有孩子的情况了。

        - case1: 删除节点为根节点的情况。先考虑根节点没有左右孩子的情况,则直接将 root == null 。再考虑根节点有一个孩子的情况,则利用到李代桃僵的技巧,将孩子节点的关键字 key 与值 value 跟root 节点的关键字 key 与值 value 分别进行交换,这样删除的节点就是孩子节点了,将根节点的左右孩子置为 null 即可。最后要不要考虑根节点有两个孩子的情况呢?

        其实是不用的,因为已经通过李代桃僵的技巧将有两个孩子节点转换为一个孩子或者没有孩子的节点。

        - case2: 删除节点不为根节点的情况。同样,先考虑删除的节点没有左右孩子的情况,也说明删除的节点就是叶子节点,所以先判断该删除节点的位置,若删除节点是左孩子,那么父亲节点的左孩子置为 null ,parent.left = null;若删除节点是右孩子,那么父亲节点的右孩子置为 null ,parent.right = null 。最后还需要将删除节点的父亲节点置为 null ,方便后续的内存自动回收。

        接着再来考虑删除节点有一个孩子的情况,删除节点设为 deleted,替换节点设为 repalce 。 先判断 deleted 的位置,若 deleted 是左孩子,则 parent.left = repalce 进行链接;若 deleted 是右孩子,则 parent.right = replace 进行链接。由于红黑树的节点与节点之间链接是相互的,所以,replace 节点的父亲节点需要链接到 parent 上, replace.parent = parent 。最后需要将删除节点删除干净,方便内存自动回收,所以 deleted.left = deleted.right = deleted.parent = null 。同样,对于删除节点有两个孩子的情况不需要考虑,已经转换为一个孩子或者没有孩子的情况了。

        删除节点之后,很有可能会导致红黑树的性质改变,所以删除节点之前或者删除节点之后,需要进行调整,使其红黑树确保性质不会发生改变。主要有以下情况:

        对于删除节点没有左右孩子来说:如果删除节点的颜色是红色,且没有左右孩子,则直接删除完之后,不会发生红黑树性质改变;如果删除节点的颜色是黑色,且没有左右孩子,则会触发黑黑相邻,直接删除会导致红黑树性质发生改变,所以需要进行复杂调整。

        对于删除节点有一个节点来说:如果删除节点的颜色为黑色,替换节点为红色,则删除节点之后,需要将替换节点的颜色变为黑色即可;如果删除节点的颜色为黑色,替换节点也为黑色,则触发了黑黑相邻,需要进行复杂的调整。

        进行复杂的调整,修复黑黑不平衡的方法 fixBlackBlack(TreeNode node)

        删除节点与剩下节点颜色都是黑色,触发双黑,双黑意思是,少了一个黑。

        - case3: 删除节点或者剩余节点的兄弟为红色,此时两个侄子定位黑色。

如图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        删除节点为节点 4 设为 deletde ,兄弟节点为节点 8 设为 brother,父亲节点为节点 6 设为 parent 。现在删除节点 4 的颜色为黑色,且剩余节点为 null 该颜色为黑色,触发双黑,又兄弟节点为红色。满足以上情况时,先判断兄弟节点的置为,若兄弟节点为右孩子,则需要将该父亲节点进行左旋;若兄弟节点为左孩子,则需要将该父亲节点进行右旋。旋转完之后,需要变色处理,将父亲节点的颜色变为红色,兄弟节点的颜色变为黑色。最后,继续将删除节点递归调用该函数。该方法的目的就是将删除节点的兄弟节点的颜色由红色变为黑色,好对以下两种情况处理。简单说明,这个方法就是一个过度的方法。

        - case4: 被调整节点的兄弟为黑色,两个侄子都为黑色

                步骤一:将兄弟节点的颜色变为红,目的是将删除节点和兄弟那边的黑色高度同时减少1

                步骤二:如果父亲是红,则需要将父亲变为黑色,避免红红相邻,此时路径黑色节点数目不变。

                步骤三:如果父亲是黑色,说明这条路径则少了一个黑,再次让父亲节点触发双黑。

如图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        调整节点为节点 1 设为 node ,兄弟节点为节点 2 设为 brother,父亲节点为节点 2 设为 parent 。该情况满足了删除节点与剩余节点都是黑色,触发双黑,且兄弟节点的颜色也为黑色,父亲节点的颜色为红色。调整的方法为:将兄弟节点的颜色变为红色,将父亲节点的颜色变为黑色即可。

调整完之后的图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        

如图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        调整节点为节点 1 设为 node ,兄弟节点为节点 3 设为 brother ,父亲节点为节点 2 设为 parent 。该情况满足调整节点与剩余节点的颜色都为黑色,触发双黑,且兄弟节点的颜色也为黑色,父亲节点也都为黑色。调整方法:将兄弟节点的颜色变为红色,再让父亲节点递归调用该函数,继续触发双黑。

调整完之后的图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        - case5: 被调整节点的兄弟为黑色,至少有一个红色节点的侄子节点。

                如果兄弟是左孩子,左侄子是红色,触发 LL 不平衡

                如果兄弟是左孩子,右侄子是红色,触发 LR 不平衡

                如果兄弟是右孩子,右侄子是红色,触发 RR 不平衡

                如果兄弟是右孩子,左侄子是红色,触发 RL 不平衡

如图: 触发 LL 不平衡情况

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        调整节点为节点 4 设为 node ,兄弟节点为节点 2 设为 brother ,父亲节点为节点 3 设为 parent ,侄子节点为节点 1 。兄弟节点与侄子节点都是左孩子,且侄子节点为红色。调整方法:将父亲节点进行右旋,再把侄子节点的颜色变为黑色,兄弟节点的颜色变为原来父亲节点的颜色,父亲节点的颜色变为黑色。

调整完之后的图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

如图:触发 LR 不平衡情况

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        调整节点为节点 4 设为 node ,父亲节点为节点 3 设为 parent ,兄弟节点为节点 1 设为 brother ,侄子节点为节点 2 。兄弟节点是左孩子,侄子为右孩子,触发了 LR 不平衡。调整方法:先将兄弟节点左旋,变成了 LL 不平衡情况。

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        再把侄子节点的颜色变为原来父亲节点的颜色,再到父亲节点的颜色变为黑色,最后右旋父亲节点即可。

调整完之后的图:

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法

        

        还有 RR 、RL 的情况与以上的情况大致是一样的,所以这里就不过多赘述了。

删除节点的代码如下:

    //查找删除节点
    private TreeNode findDelete(int key) {
        TreeNode p = root;
        while(p != null) {
            if (p.key > key) {
                p = p.left;
            } else if (p.key < key) {
                p = p.right;
            }else {
                return p;
            }
        }
        //若没有找到则返回null
        return null;
    }

    //查找剩余节点
    private TreeNode findReplaced(TreeNode deleted) {
        //没有孩子的情况:
        if (deleted.left == null && deleted.right == null) {
            return null;
        }
        if (deleted.left == null) {
            return deleted.right;
        }
        if (deleted.right == null) {
            return deleted.left;
        }

        //有两个孩子的情况,找后继节点即可
        TreeNode p = deleted.right;
        while(p.left != null) {
            p = p.left;
        }
        return p;
    }

    //删除节点
    //正常删除节点,遇到黑黑不平衡则需要进行调整
    public void remove(int key) {
        TreeNode delete = findDelete(key);
        if (delete == null) {
            return;
        }
        doRemove(delete);

    }

    private void doRemove(TreeNode deleted) {
        TreeNode replaced = findReplaced(deleted);
        TreeNode parent = deleted.parent;
        //没有孩子的情况:
        if (replaced == null) {
            //删除的节点为根节点情况下:
            if (deleted == root) {
                root = null;
                return;
            }else {
                if (isRed(deleted)) {
                    //无需任何操作
                }else {
                    //触发黑黑不平衡,需要进行复杂的操作
                    fixBlackBlack(deleted);
                }
                if (deleted.isLeftChild()) {
                    parent.left = null;
                }else {
                    parent.right = null;
                }
                deleted.parent = null;
            }
            return;
        }
        //有一个孩子的情况
        if (deleted.left == null || deleted.right == null) {
            if (deleted == root) {
                root.key = replaced.key;
                root.value = replaced.value;
                root.left = root.right = null;
            }else {
                if (deleted.isLeftChild()) {
                    parent.left = replaced;
                } else {
                    parent.right = replaced;
                }
                replaced.parent = parent;
                deleted.left = deleted.right = deleted.parent = null;

                if (isRed(replaced) && isBlack(deleted)) {
                    //却少一个黑色,则将替换的节点换为红色即可
                    replaced.color = BLACK;
                }else {
                    //遇到黑黑不平衡情况,则需要进行复杂调整
                    fixBlackBlack(replaced);
                }
            }
            return;
        }
        //有两个孩子的情况,需要将用到李代桃僵技巧
        int key = deleted.key;
        deleted.key = replaced.key;
        replaced.key = key;

        Object value = deleted.value;
        deleted.value = replaced.value;
        replaced.value = value;
        doRemove(replaced);

    }
    private void fixBlackBlack(TreeNode node) {
        if (node == root) {
            return;
        }
        TreeNode parent = node.parent;
        TreeNode brother = node.brother();

        if (isRed(node.brother())) {
            //先进行旋转调整,再换色暂时达到平衡
            if (brother.isLeftChild()) {
                rightRotate(parent);
            }else {
                leftRotate(parent);
            }
            parent.color = RED;
            brother.color = BLACK;
            fixBlackBlack(node);
            return;
        }
        //两个侄子都为黑色
        if (brother == null) {
            fixBlackBlack(parent);
        }else {
            //case 4 兄弟是黑色,两个侄子也是黑色
            if (isBlack(brother.left) && isBlack(brother.right)) {
                brother.color = RED;
                if (isRed(parent)) {
                    parent.color = BLACK;
                }else {
                    fixBlackBlack(parent);
                }
            }

            //case 5 兄弟是黑色,侄子有红色
            else {
                //其中某一个侄子不为黑色
                //兄弟为左孩子、侄子为左孩子,触发 ll
                if (brother.isLeftChild() && isRed(brother.left)) {
                    rightRotate(parent);
                    brother.left.color = BLACK;
                    brother.color = parent.color;
                    parent.color = BLACK;
                } else if (brother.isLeftChild() && isRed(brother.right)) {
                    //兄弟为左孩子、侄子为右孩子,先触发 lr
                    //需要将 lr 转变为 ll 情况再处理
                    brother.right.color = parent.color;
                    leftRotate(brother);
                    rightRotate(parent);
                    parent.color = BLACK;
                } else if ( !brother.isLeftChild() && isRed(brother.right)) {
                    //兄弟为右孩子,侄子为右孩子,触发 rr
                    leftRotate(parent);
                    brother.right.color = BLACK;
                    brother.color = parent.color;
                    parent.color = BLACK;
                }else {
                    //最后一种情况兄弟为右孩子、侄子为左孩子,触发 rl
                    //需要将 rl 转变为 rr 情况再处理
                    brother.left.color = parent.color;
                    rightRotate(brother);
                    leftRotate(parent);
                    parent.color = BLACK;

                }
            }
        }

    }

        5.0 实现红黑树核心方法的完整代码

import static TreeNode.RedBlackTree.Color.BLACK;
import static TreeNode.RedBlackTree.Color.RED;

public class RedBlackTree {

    enum Color {
        RED,BLACK;
    }

    private TreeNode root;

    private static class TreeNode {
        int key;
        Object value;
        TreeNode left;
        TreeNode right;
        TreeNode parent;
        Color color = RED;

        //构造方法
        public TreeNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public TreeNode(int key, Object value, TreeNode left, TreeNode right, TreeNode parent) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }

        //判断是否为左孩子
         public boolean isLeftChild() {
             return parent != null && parent.left == this;
         }

         //获取叔叔节点
         public TreeNode uncle() {
             if (this.parent == null || this.parent.parent == null) {
                 return null;
             }
             if (this.isLeftChild()) {
                 return this.parent.parent.right;
             } else {
                 return this.parent.parent.left;
             }
         }

         //获取兄弟节点
         public TreeNode brother() {
             if (this.parent == null) {
                 return null;
             }
             if (this.isLeftChild()) {
                 return this.parent.right;
             }else {
                 return this.parent.left;
             }
         }


    }

    //判断是否为红色节点
    private boolean isRed(TreeNode node) {
        return node != null && node.color == RED;
    }

    //判断是否为黑色节点
    private  boolean isBlack(TreeNode node) {
        return node == null || node.color == BLACK;
    }

    //右旋
    //1.考虑旋转后节点的维护parent 2.重新与上一个节点建立联系
    private void rightRotate(TreeNode node) {
        TreeNode parent = node.parent;
        TreeNode nodeLeft = node.left;
        TreeNode nodeLeftRight = nodeLeft.right;
        if (nodeLeftRight != null) {
            nodeLeftRight.parent = node;
        }
        nodeLeft.right = node;
        nodeLeft.parent = parent;
        node.left = nodeLeftRight;
        node.parent = nodeLeft;
        if (parent == null) {
            root = nodeLeft;
        } else if (parent.left == node) {
            parent.left = nodeLeft;
        }else {
            parent.right = nodeLeft;
        }

    }

    //左旋
    //1.考虑旋转后节点的维护parent 2.重新与上一个节点建立联系
    private void leftRotate(TreeNode node) {
        TreeNode parent = node.parent;
        TreeNode nodeRight = node.right;
        TreeNode nodeRightLeft = nodeRight.left;
        if (nodeRightLeft != null) {
            nodeRightLeft.parent = node;
        }
        nodeRight.left = node;
        nodeRight.parent = parent;
        node.right = nodeRightLeft;
        node.parent = nodeRight;
        //2.重新与上一个节点建立联系
        if (parent == null) {
            root = nodeRight;
        } else if (parent.left == node) {
            parent.left = nodeRight;
        }else {
            parent.right = nodeRight;
        }

    }

    //更新、增添节点
    //正常更新、删除,遇到红红不平衡则需要进行调整
    public void put (int key, Object value) {
        TreeNode p = root;
        TreeNode parent = null;
        while (p != null) {
            parent = p;
            if (p.key > key) {
                p = p.left;
            }else if (p.key < key) {
                p = p.right;
            }else {
                p.value = value;
                return;
            }
        }
        TreeNode node = new TreeNode(key,value);
        if (parent == null) {
            root = node;
        }else {
            if (node.key > parent.key) {
                parent.right = node;

            }else {
                parent.left = node;

            }
            node.parent = parent;
        }
        //可能会发生红红不平衡,则需要调整
        fixRedRed(node);

    }
    //调整红红不平衡

    private void fixRedRed(TreeNode node) {
        //case1: 插入节点为根节点,将根节点变黑
        if(node == root) {
            node.color = BLACK;
            return;
        }
        if (isBlack(node.parent)) {
            //case2:插入节点的父亲若为黑,树的红黑性质不变,无需调整
            //无需调整
            return;
        }
        // 插入节点的父亲为红色,触发红红相邻
        //case3:叔叔为红色
        TreeNode parent = node.parent;
        TreeNode grandparent = parent.parent;
        TreeNode uncle = node.uncle();
        if (isRed(uncle)) {
            //进行变色处理即可
            //将其父亲、叔叔变为黑色,爷爷变为红色
            //若爷爷触发了红红,则继续递归调用该函数
            parent.color = BLACK;
            uncle.color = BLACK;
            grandparent.color = RED;
            fixRedRed(grandparent);
            return;
        }
        
        //case4:叔叔为黑色
        //该父亲为左孩子,该插入点也为左孩子,则触发 ll
        if (parent.isLeftChild() && node.isLeftChild()) {
            //先将父亲变为黑色、爷爷变为红色,再右旋转
            parent.color = BLACK;
            grandparent.color = RED;
            rightRotate(grandparent);
        }else if (parent.isLeftChild()) {
            //该插入节点为右孩子、该父亲为左孩子,则触发 lr
            //先左旋变为 ll 情况
            leftRotate(parent);
            node.color = BLACK;
            grandparent.color = RED;
            rightRotate(grandparent);
        } else if (!node.isLeftChild()) {
            //插入节点为右孩子、父亲节点也为右孩子 rr
            parent.color = BLACK;
            grandparent.color = RED;
            leftRotate(grandparent);
        }else {
            //插入节点为左孩子、父亲节点为右孩子 rl
            rightRotate(parent);
            node.color = BLACK;
            grandparent.color = RED;
            leftRotate(grandparent);

        }
    }

    //查找删除节点
    private TreeNode findDelete(int key) {
        TreeNode p = root;
        while(p != null) {
            if (p.key > key) {
                p = p.left;
            } else if (p.key < key) {
                p = p.right;
            }else {
                return p;
            }
        }
        //若没有找到则返回null
        return null;
    }

    //查找剩余节点
    private TreeNode findReplaced(TreeNode deleted) {
        //没有孩子的情况:
        if (deleted.left == null && deleted.right == null) {
            return null;
        }
        if (deleted.left == null) {
            return deleted.right;
        }
        if (deleted.right == null) {
            return deleted.left;
        }

        //有两个孩子的情况,找后继节点即可
        TreeNode p = deleted.right;
        while(p.left != null) {
            p = p.left;
        }
        return p;
    }

    //删除节点
    //正常删除节点,遇到黑黑不平衡则需要进行调整
    public void remove(int key) {
        TreeNode delete = findDelete(key);
        if (delete == null) {
            return;
        }
        doRemove(delete);

    }

    private void doRemove(TreeNode deleted) {
        TreeNode replaced = findReplaced(deleted);
        TreeNode parent = deleted.parent;
        //没有孩子的情况:
        if (replaced == null) {
            //删除的节点为根节点情况下:
            if (deleted == root) {
                root = null;
                return;
            }else {
                if (isRed(deleted)) {
                    //无需任何操作
                }else {
                    //触发黑黑不平衡,需要进行复杂的操作
                    fixBlackBlack(deleted);
                }
                if (deleted.isLeftChild()) {
                    parent.left = null;
                }else {
                    parent.right = null;
                }
                deleted.parent = null;
            }
            return;
        }
        //有一个孩子的情况
        if (deleted.left == null || deleted.right == null) {
            if (deleted == root) {
                root.key = replaced.key;
                root.value = replaced.value;
                root.left = root.right = null;
            }else {
                if (deleted.isLeftChild()) {
                    parent.left = replaced;
                } else {
                    parent.right = replaced;
                }
                replaced.parent = parent;
                deleted.left = deleted.right = deleted.parent = null;

                if (isRed(replaced) && isBlack(deleted)) {
                    //却少一个黑色,则将替换的节点换为红色即可
                    replaced.color = BLACK;
                }else {
                    //遇到黑黑不平衡情况,则需要进行复杂调整
                    fixBlackBlack(replaced);
                }
            }
            return;
        }
        //有两个孩子的情况,需要将用到李代桃僵技巧
        int key = deleted.key;
        deleted.key = replaced.key;
        replaced.key = key;

        Object value = deleted.value;
        deleted.value = replaced.value;
        replaced.value = value;
        doRemove(replaced);

    }
    private void fixBlackBlack(TreeNode node) {
        if (node == root) {
            return;
        }
        TreeNode parent = node.parent;
        TreeNode brother = node.brother();

        if (isRed(node.brother())) {
            //先进行旋转调整,再换色暂时达到平衡
            if (brother.isLeftChild()) {
                rightRotate(parent);
            }else {
                leftRotate(parent);
            }
            parent.color = RED;
            brother.color = BLACK;
            fixBlackBlack(node);
            return;
        }
        //两个侄子都为黑色
        if (brother == null) {
            fixBlackBlack(parent);
        }else {
            //case 4 兄弟是黑色,两个侄子也是黑色
            if (isBlack(brother.left) && isBlack(brother.right)) {
                brother.color = RED;
                if (isRed(parent)) {
                    parent.color = BLACK;
                }else {
                    fixBlackBlack(parent);
                }
            }

            //case 5 兄弟是黑色,侄子有红色
            else {
                //其中某一个侄子不为黑色
                //兄弟为左孩子、侄子为左孩子,触发 ll
                if (brother.isLeftChild() && isRed(brother.left)) {
                    rightRotate(parent);
                    brother.left.color = BLACK;
                    brother.color = parent.color;
                    parent.color = BLACK;
                } else if (brother.isLeftChild() && isRed(brother.right)) {
                    //兄弟为左孩子、侄子为右孩子,先触发 lr
                    //需要将 lr 转变为 ll 情况再处理
                    brother.right.color = parent.color;
                    leftRotate(brother);
                    rightRotate(parent);
                    parent.color = BLACK;
                } else if ( !brother.isLeftChild() && isRed(brother.right)) {
                    //兄弟为右孩子,侄子为右孩子,触发 rr
                    leftRotate(parent);
                    brother.right.color = BLACK;
                    brother.color = parent.color;
                    parent.color = BLACK;
                }else {
                    //最后一种情况兄弟为右孩子、侄子为左孩子,触发 rl
                    //需要将 rl 转变为 rr 情况再处理
                    brother.left.color = parent.color;
                    rightRotate(brother);
                    leftRotate(parent);
                    parent.color = BLACK;

                }
            }
        }

    }

}

                                        

                                                   - - - 努力要成为自己想要称为的人

Java 数据结构篇-实现红黑树的核心方法,Java 数据结构与算法篇,数据结构,java,r-tree,算法文章来源地址https://www.toymoban.com/news/detail-820181.html

到了这里,关于Java 数据结构篇-实现红黑树的核心方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构——常见二叉树的分类(完全二叉树、满二叉树、平衡二叉树、二叉搜索树、红黑树)

    专业术语 中文 描述 Root 根节点 一棵树的顶点 Child 孩子结点 一个结点含有的子树的根节点称为该结点的子节点 Leaf 叶子结点 没有孩子的节点 Degree 度 一个节点包含子树的数量 Edge 边 一个节点与另外一个节点的连接 Depth 深度 根节点到这个节点经过边的数量 Height 节点高度 从

    2024年02月03日
    浏览(44)
  • 【数据结构】—红黑树(C++实现)

                                                            🎬 慕斯主页 : 修仙—别有洞天                                                  💜 本文前置知识:  AVL树                                                       ♈️ 今日夜电波 :

    2024年02月05日
    浏览(51)
  • 【数据结构】红黑树(C++实现)

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 《算法》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.概念 2.性质 3.节点的定义  4.插入 4.1情况1:叔叔u存在且为红 4.2情况2:

    2024年03月13日
    浏览(54)
  • Java常见的数据结构:栈、队列、数组、链表、二叉树、二叉查找树、平衡二叉树、红黑树

    数据结构是计算机底层存储、组织数据的方式。是指数据相互之间是以什么方式排列在一起的。 通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率 栈 队列 数组 链表 二叉树 二叉查找树 平衡二叉树 红黑树... 后进先出,先进后出 数据进入栈模型的过程称为

    2024年02月07日
    浏览(46)
  • 数据结构 | 红黑树

    节点的左边比节点的值小,右边比节点的值大。 节点要么是 红色 ,要么是 黑色 根节点 是黑色 叶子节点都是黑色的空节点 红黑树中红色节点的子节点都是黑色 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点 在添加或者删除节点的时候,如果不满足这些性质会

    2024年01月21日
    浏览(43)
  • 数据结构——红黑树

    目录 概念 性质 结点的定义  插入 调整 当p是g的左孩子时 当p为g的右孩子时 插入完整代码 红黑树的检测 红黑树完整代码(包括测试数据)   红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RED或BLACK。 通过对任何一条从根到叶子的路径

    2023年04月09日
    浏览(45)
  • 【数据结构】红黑树

    🐱作者:一只大喵咪1201 🐱专栏:《数据结构与算法》 🔥格言: 你只管努力,剩下的交给时间! 在学习AVL树的时候,我们知道,当修改AVL树的结构(插入,删除)时,会通过旋转来保证平衡因子不超过1,所以频繁的修改结构会导致效率低下,今天我们学习的红黑树就完美解

    2023年04月17日
    浏览(48)
  • 【数据结构-树】红黑树

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年02月07日
    浏览(43)
  • [数据结构]-红黑树

    前言 作者 : 小蜗牛向前冲 名言: 我可以接受失败,但我不能接受放弃   如果觉的博主的文章还不错的话,还请 点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、红黑树的基本知识  1、红黑树的概念 2、性质  二、红黑树的模拟实

    2024年02月04日
    浏览(45)
  • 红黑树数据结构

    现在JAVASE中HashMap中底层源码是由数组+链表+红黑树进行设计的,然后很多地方也是用到红黑树,这里单独对红黑树数据结构进行简单的介绍。 目录 红黑树概念 红黑树的性质 自平衡规则 代码   红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可

    2024年02月01日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包