一天一个算法——>红黑树JAVA实现

简介: 一天一个算法——>红黑树JAVA实现

代码均为自己的思路,手动敲写,如有bug,或者思路错误,欢迎指正,多多交流。

package tree;
/**
 * 红黑树(R-B Tree)
 * 递归方式空间复杂度为O(log n),且受栈内存限制,故能使用循环的尽量使用循环,本例子使用while循环
 * 这里只模拟int类型实现,如果需要其他类型,请将int类型修改为泛型,并实现extends Comparable<T>接口,方便比较compareTo
 * 动态模拟实现:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
 * GitHub地址:https://github.com/hack-feng/algorithm
 * 理论知识:https://blog.csdn.net/qq_34988304/article/details/100135759
 * CSDN博客地址:
 * 联系作者:1150640979@qq.com
 *
 * 红黑树的特性:
 * (1)每个节点或者是黑色,或者是红色。
 * (2)根节点是黑色。
 * (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
 * (4)如果一个节点是红色的,则它的子节点必须是黑色的。
 * (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
 */
public class RedBlackTree {
    private RBNode root;    // 根结点
    private static final boolean RED   = false;
    private static final boolean BLACK = true;
    static class RBNode{
        // 节点颜色, 红:false   黑:true
        boolean color;
        int data;
        RBNode left;
        RBNode right;
        RBNode parent;
        RBNode(int data, RBNode left, RBNode right){
            this.data = data;
            this.left = left;
            this.right = right;
        }
    }
    private void insert(int data){
        RBNode node = new RBNode(data, null, null);
        insert(node);
    }
    private void insert(RBNode node){
        // 定义node的根节点
        RBNode pNode = null;
        // 定义一个临时节点
        RBNode tempNode = this.root;
        // 取出root的根节点
        while(tempNode != null){
            pNode = tempNode;
            if(tempNode.data > node.data){
                tempNode = tempNode.left;
            }else{
                tempNode = tempNode.right;
            }
        }
        // 设置node的父节点
        node.parent = pNode;
        // 判断将node放在其父节点的左节点还是右节点
        if(pNode != null){
            if(pNode.data > node.data){
                pNode.left = node;
            }else{
                pNode.right = node;
            }
        }else{
            this.root = node;
        }
        // 初始化节点的颜色为红色
        node.color = RED;
        // 修正为红黑树
        insertFixUp(node);
    }
    /**
     * 修正红黑树
     * 分为一下几种情况:
     * 1:如果是父节点,直接变成黑色
     * 2:如果父节点是黑色,不违背特性,直接添加即可
     * 3:如果父节点是红色,添加违背特性。则又分为一下几种情况:
     *  3.1:父节点是祖父节点的左节点
     *      3.1.1:如果叔叔节点为黑的时候
     *          3.1.1.1:如果插入的节点是父节点的左节点
     *              父节点变色,祖父节点变色,祖父节点右旋
     *          3.1.1.2:如果插入的节点是父节点的右节点
     *              父节点左旋,然后父节点变色,祖父节点变色,祖父节点右旋
     *      3.1.2:如果叔叔节点为红的时候
     *          直接将父节点和叔叔节点涂黑,祖父节点涂红就可以了
     *  3.2:父节点是祖父节点的右节点
     *      3.2.1:如果叔叔节点为黑的时候
     *          3.2.1.1:如果插入的节点是父节点的左节点
     *              父节点变色,祖父节点变色,祖父节点左旋
     *          3.2.1.2:如果插入的节点是父节点的右节点
     *              父节点右旋,然后父节点变色,祖父节点变色,祖父节点左旋
     *      3.2.2:如果叔叔节点为红的时候
     *          直接将父节点和叔叔节点涂黑,祖父节点涂红就可以了
     */
    private void insertFixUp(RBNode node) {
        RBNode pNode, gNode;
        // 如果父节点不为空,并且父节点的颜色是红色,则会触发树旋转
        while((pNode = node.parent) != null && isRed(pNode.color)){
            gNode = pNode.parent;
            // 父节点为祖父节点的左节点时
            if(gNode.left == pNode){
                RBNode uNode = gNode.right;
                //叔叔节点为红色
                if(uNode != null && isRed(uNode.color)){
                    pNode.color = BLACK;
                    uNode.color = BLACK;
                    gNode.color = RED;
                    node = gNode;
                }else{
                    // 叔叔节点为黑色,当前节点在父节点的右节点
                    if(pNode.right == node){
                        // 先把父节点左旋
                        leftRotate(pNode);
                    }
                    // 叔叔节点为黑色,当前节点在父节点的左节点
                    pNode.color = BLACK;
                    gNode.color = RED;
                    rightRotate(gNode);
                }
            }else{
                RBNode uNode = gNode.left;
                //叔叔节点为红色
                if(uNode != null && isRed(uNode.color)){
                    pNode.color = BLACK;
                    uNode.color = BLACK;
                    gNode.color = RED;
                    node = gNode;
                }else{
                    // 叔叔节点为黑色,当前节点在父节点的左节点
                    if(pNode.left == node){
                        // 先把父节点右旋
                        rightRotate(pNode);
                    }
                    // 叔叔节点为黑色,当前节点在父节点的右节点
                    pNode.color = BLACK;
                    gNode.color = RED;
                    leftRotate(gNode);
                }
            }
        }
        // 将根节点设为黑色
        this.root.color = BLACK;
    }
    // 右旋
    private void rightRotate(RBNode node){
        // 取出当前节点的左节点始终,赋值给leftNode
        RBNode leftNode = node.left;
        // 将leftNode的右节点赋值给node的左节点
        node.left = leftNode.right;
        // 因为下面leftNode.right = node, 所以如果leftNode.right != null,修改leftNode.right.parent = node
        if(leftNode.right != null){
            leftNode.right.parent = node;
        }
        // 设置leftNode.parent
        leftNode.parent = node.parent;
        if(node.parent == null){
            // 如果node.parent为null,说明node原来为根节点,则将leftNode设为新的根节点
            this.root = leftNode;
        }else{
            // 将指向node的父节点更改为leftNode
            if(node.parent.right == node){
                node.parent.right = leftNode;
            }else{
                node.parent.left = leftNode;
            }
        }
        // 设置leftNode.right
        leftNode.right = node;
        // 设置node.parent
        node.parent = leftNode;
    }
    // 左旋,解释同右旋
    private void leftRotate(RBNode node){
        RBNode rightNode = node.right;
        node.right = rightNode.left;
        if(rightNode.left != null){
            rightNode.left.parent = node;
        }
        rightNode.parent = node.parent;
        if(node.parent == null){
            this.root = rightNode;
        }else{
            if(node.parent.left == node){
                node.parent.left = rightNode;
            }else{
                node.parent.right = rightNode;
            }
        }
        rightNode.left = node;
        node.parent = rightNode;
    }
    private boolean isRed(boolean color){
        return !color;
    }
    public static void main(String[] args){
        RedBlackTree rbTree = new RedBlackTree();
//        int[] dataArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8};
//        int[] dataArray = new int[]{8, 7, 6, 5, 4, 3, 2, 1};
        // 测试
        int[] dataArray = new int[]{3, 5, 8, 4, 7, 1, 6, 2};
        for (int i : dataArray) {
            rbTree.insert(i);
        }
        System.out.println(123);
    }
}
目录
相关文章
|
7天前
|
监控 算法 网络协议
Java 实现局域网电脑屏幕监控算法揭秘
在数字化办公环境中,局域网电脑屏幕监控至关重要。本文介绍用Java实现这一功能的算法,涵盖图像采集、数据传输和监控端显示三个关键环节。通过Java的AWT/Swing库和Robot类抓取屏幕图像,使用Socket进行TCP/IP通信传输图像数据,并利用ImageIO类在监控端展示图像。整个过程确保高效、实时和准确,为提升数字化管理提供了技术基础。
40 15
|
3月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
101 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
6天前
|
存储 算法 安全
基于红黑树的局域网上网行为控制C++ 算法解析
在当今网络环境中,局域网上网行为控制对企业和学校至关重要。本文探讨了一种基于红黑树数据结构的高效算法,用于管理用户的上网行为,如IP地址、上网时长、访问网站类别和流量使用情况。通过红黑树的自平衡特性,确保了高效的查找、插入和删除操作。文中提供了C++代码示例,展示了如何实现该算法,并强调其在网络管理中的应用价值。
|
13天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
25 6
|
3月前
|
Java 开发者
在Java的集合世界里,Set以其独特的特性脱颖而出,它通过“哈希魔法”和“红黑树防御”两大绝技
【10月更文挑战第13天】在Java的集合世界里,Set以其独特的特性脱颖而出。它通过“哈希魔法”和“红黑树防御”两大绝技,有效抵御重复元素的侵扰,确保集合的纯洁性和有序性。无论是“人海战术”还是“偷梁换柱”,Set都能从容应对,成为开发者手中不可或缺的利器。
35 6
|
3月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
161 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
3月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
98 2
|
3月前
|
算法 Java Linux
java制作海报一:java使用Graphics2D 在图片上写字,文字换行算法详解
这篇文章介绍了如何在Java中使用Graphics2D在图片上绘制文字,并实现自动换行的功能。
166 0
|
3月前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
43 0
|
5月前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
74 2