红黑树java实现

红黑树的性质

红黑树是一课二叉搜索树,它在每个结点上增加了一个存储位来表示结点的颜色,可以使RED或BLACK。通过对任何一条从根到叶子的简单路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因而是近似平衡的

树中每个结点包含5个属性:color、key、left、right、parent。如果一个结点没有子结点或父结点,则该结点相应属性的值为null。一棵红黑树是满足下面红黑性质的二叉搜索树:

  1. 每个结点或是红色的,或是黑色的;
  2. 根结点是黑色的;
  3. 每个叶结点是黑色的;
  4. 如果一个结点是红色的,则它的两个子结点都是黑色的;
  5. 对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

为了便于处理红黑树代码中的边界条件,可以使用一个哨兵来代表null。对于一棵红黑树,哨兵是一个与树中的普通结点有相同属性的对象。它的color属性为BLACK,其他属性可以设置为任意值。如下图所示为一棵使用了哨兵的红黑树示例。
在这里插入图片描述

旋转

当对树执行插入或操作时,对树做了修改,所做的修改可能会导致树不满足红黑树的性质,因此需要进行旋转来维护红黑树的性质。旋转可分为左旋和右旋。下图展示了一棵子树旋转后的结果。
在这里插入图片描述

java代码实现

下面是使用java简单实现了红黑树,以及红黑树的插入和删除操作。

/**
 * 数据结构-红黑树
 */
public class RBTree {
    /**
     * 哨兵
     */
    private final Node sentinel = new Node(Color.BLACK);

    /**
     * 根节点
     */
    private Node root = sentinel;

    /**
     * 根据关键字搜索节点
     *
     * @param key 关键字
     * @return key所在的节点
     */
    public Node search(int key) {
        Node node = root;
        while (node != null && key != node.key) {
            // 如果key小于当前节点的key,则往左孩子节点找,否则往右孩子节点找
            if (key < node.key) {
                node = node.left;
            } else {
                node = node.right;
            }
        }
        return node;
    }

    /**
     * 插入关键字为key的节点
     *
     * @param key 关键字
     */
    public void insert(int key) {
        Node y = sentinel;
        Node x = root;
        while (x != sentinel) {
            y = x;
            if (key < x.key) {
                x = x.left;
            } else {
                x = x.right;
            }
        }
        Node newNode = new Node();
        newNode.key = key;
        newNode.parent = y;
        if (y == sentinel) {
            root = newNode;
        } else if (key < y.key) {
            y.left = newNode;
        } else {
            y.right = newNode;
        }
        newNode.left = sentinel;
        newNode.right = sentinel;
        newNode.color = Color.RED;
        insertFixUp(newNode);
    }


    /**
     * 删除关键字为key的节点
     *
     * @param key 关键字
     */
    public void delete(int key) {
        Node node = search(key);
        Node y = node;
        Color yOriginalColor = y.color;
        Node x;
        if (node.left == sentinel) {
            x = node.right;
            transplant(node, node.right);
        } else if (node.right == sentinel) {
            x = node.left;
            transplant(node, node.left);
        } else {
            y = min(node.right);
            yOriginalColor = y.color;
            x = y.right;
            if (y.parent == node) {
                x.parent = y;
            } else {
                transplant(y, y.right);
                y.right = node.right;
                y.right.parent = y;
            }
            transplant(node, y);
            y.left = node.left;
            y.left.parent = y;
            y.color = node.color;
        }
        if (yOriginalColor == Color.BLACK) {
            deleteFixUp(x);
        }
    }

    /**
     * 获取以node为根的子树中key最小的节点
     *
     * @param node 子树根节点
     * @return 子树中key最小的节点
     */
    public Node min(Node node) {
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }

    public Node getRoot() {
        return root;
    }

    /**
     * 删除一个节点后,维护树的红黑性质
     *
     * @param node 节点
     */
    private void deleteFixUp(Node node) {
        while (node != root && node.color == Color.BLACK) {
            if (node == node.parent.left) {
                Node w = node.parent.right;
                if (w.color == Color.RED) {
                    w.color = Color.BLACK;
                    node.parent.color = Color.RED;
                    leftRotate(node.parent);
                    w = node.parent.right;
                }
                if (w.left.color == Color.BLACK && w.right.color == Color.BLACK) {
                    w.color = Color.RED;
                    node = node.parent;
                } else if (w.right.color == Color.BLACK) {
                    w.left.color = Color.BLACK;
                    w.color = Color.RED;
                    rightRotate(w);
                    w = node.parent.right;
                }
                w.color = node.parent.color;
                node.parent.color = Color.BLACK;
                w.right.color = Color.BLACK;
                leftRotate(node.parent);
                node = root;
            } else {
                Node w = node.parent.left;
                if (w.color == Color.RED) {
                    w.color = Color.BLACK;
                    node.parent.color = Color.RED;
                    leftRotate(node.parent);
                    w = node.parent.left;
                }
                if (w.right.color == Color.BLACK && w.left.color == Color.BLACK) {
                    w.color = Color.RED;
                    node = node.parent;
                } else if (w.left.color == Color.BLACK) {
                    w.right.color = Color.BLACK;
                    w.color = Color.RED;
                    leftRotate(w);
                    w = node.parent.left;
                }
                w.color = node.parent.color;
                node.parent.color = Color.BLACK;
                w.left.color = Color.BLACK;
                rightRotate(node.parent);
                node = root;
            }
        }
        node.color = Color.BLACK;
    }

    /**
     * 将node1替换成node2,以支持删除操作
     *
     * @param node1 节点1
     * @param node2 节点2
     */
    private void transplant(Node node1, Node node2) {
        if (node1.parent == sentinel) {
            root = node2;
        } else if (node1 == node1.parent.left) {
            node1.parent.left = node2;
        } else {
            node1.parent.right = node2;
        }
        node2.parent = node1.parent;
    }

    /**
     * 插入一个节点后,维护树的红黑性质
     *
     * @param node 插入的节点
     */
    private void insertFixUp(Node node) {
        while (node.parent.color == Color.RED) {
            if (node.parent == node.parent.parent.left) {
                Node temp = node.parent.parent.right;
                if (temp.color == Color.RED) {
                    node.parent.color = Color.BLACK;
                    temp.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    node = node.parent.parent;
                } else if (node == node.parent.right) {
                    node = node.parent;
                    leftRotate(node);
                } else {
                    node.parent.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    rightRotate(node.parent.parent);
                }
            } else {
                Node temp = node.parent.parent.left;
                if (temp.color == Color.RED) {
                    node.parent.color = Color.BLACK;
                    temp.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    node = node.parent.parent;
                } else if (node == node.parent.left) {
                    node = node.parent;
                    rightRotate(node);
                } else {
                    node.parent.color = Color.BLACK;
                    node.parent.parent.color = Color.RED;
                    leftRotate(node.parent.parent);
                }
            }
        }
        root.color = Color.BLACK;
    }

    /**
     * 在节点node上进行左旋操作
     *
     * @param node 节点
     */
    private void leftRotate(Node node) {
        Node right = node.right;
        // 将node的右子树的左子树放到node的右子树位置
        node.right = right.left;
        if (right.left != sentinel) {
            right.left.parent = node;
        }
        // 设置node的右子树的父节点为node的父节点
        right.parent = node.parent;
        if (node.parent == sentinel) {
            root = right;
        } else if (node == node.parent.left) {
            node.parent.left = right;
        } else {
            node.parent.right = right;
        }
        // 将node放到node的右子树的左子树位置
        right.left = node;
        node.parent = right;
    }

    /**
     * 在节点node上进行右旋操作
     *
     * @param node 节点
     */
    private void rightRotate(Node node) {
        Node left = node.left;
        node.left = left.right;
        if (left.right != sentinel) {
            left.right.parent = node;
        }
        left.parent = node.parent;
        if (node.parent == sentinel) {
            root = left;
        } else if (node == node.parent.right) {
            node.parent.right = left;
        } else {
            node.parent.left = left;
        }
        left.right = node;
        node.parent = left;
    }

    private enum Color {
        /**
         * 红色
         */
        RED,

        /**
         * 黑色
         */
        BLACK
    }

    public static class Node {
        /**
         * 节点颜色
         */
        private Color color;

        /**
         * 节点关键字
         */
        private int key;

        /**
         * 左孩子节点
         */
        private Node left;

        /**
         * 右孩子节点
         */
        private Node right;

        /**
         * 父节点
         */
        private Node parent;

        private Node() {
        }

        private Node(Color color) {
            this.color = color;
        }

        public Color getColor() {
            return color;
        }

        public int getKey() {
            return key;
        }

        public Node getLeft() {
            return left;
        }

        public Node getRight() {
            return right;
        }

        public Node getParent() {
            return parent;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/179408.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT规模化服务的经验与教训

2022年11月30日&#xff0c;OpenAI发布ChatGPT&#xff0c;以很多人未曾预料的速度迅速走红。与此同时&#xff0c;由于短时间内用户量的暴涨&#xff0c;导致服务器过载&#xff0c;迫使OpenAI停止新用户的注册。 ChatGPT发布这一年&#xff0c;同样的情景发生了好几次。在最近…

RT-Thread 线程间同步【信号量、互斥量、事件集】

线程间同步 一、信号量1. 创建信号量2. 获取信号量3. 释放信号量4. 删除信号量5. 代码示例 二、互斥量1. 创建互斥量2. 获取互斥量3. 释放互斥量4. 删除互斥量5. 代码示例 三、事件集1. 创建事件集2. 发送事件3. 接收事件4. 删除事件集5. 代码示例 简单来说&#xff0c;同步就是…

基于PCA算法的点云平面拟合

平面拟合 1、平面拟合2、参考文献3、相关代码 1、平面拟合 PCA 是一种数学变换的方法&#xff0c;利用降维的思想在变换中保持变量的总方差不变&#xff0c;将给定的一组变量线性变换为另一组不相关的变量&#xff0c;并且使变换后的第一变量的方差最大&#xff0c;即第一主成分…

2023亚太杯数学建模赛题人工精准翻译

大家好&#xff0c;亚太杯今天早上6点已经开赛啦&#xff0c;然后我在这里给大家带来赛题的精准人工翻译&#xff0c;防止大家直接用软件翻译导致某些地方乱码或者翻译不精准&#xff0c;这会导致后续做题过程出现很大偏差。 注意&#xff0c;以下翻译均免费发放word形式的哈&…

使用websocket获取thingsboard设备的实时数据

背景 有一个读者前来咨询,如何实时获取设备的遥测数据。 其实tb是有提供websocket接口来获取设备数据的。而且还支持js跨域调用。下面给大家演示一下。 websocket地址 完整代码 <!DOCTYPE HTML> <html><h

ImportError: cannot import name ‘contextfilter‘ from ‘jinja2‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

redis的主从复制,哨兵模式

1.主从复制 主从复制&#xff1a;主从复制是redis实现高可用的基础&#xff0c;哨兵模式和集群都是在主从复制的基础之上实现高可用 主从复制实现数据的多机备份&#xff0c;以及读写分离&#xff08;主服务器负责写&#xff0c;从服务器只能读&#xff09; 缺陷&#xff1a…

让SOME/IP运转起来——SOME/IP系统设计(下)之数据库开发

上一篇我们介绍了SOME/IP矩阵的设计流程&#xff0c;这一篇重点介绍如何把SOME/IP矩阵顺利的交给下游软件团队进行开发。 车载以太网通信矩阵开发完成后&#xff0c;下一步应该做什么&#xff1f; 当我们完成SOME/IP矩阵开发&#xff0c;下一步需要把开发完成的矩阵换成固定格…

10.docker的网络network-概述

1.docker的网络模式 docker共有四种网路模式&#xff0c;分别是bridge、host、none和container. 1.1 bridge bridge,也称为虚拟网桥。在bridge模式下&#xff0c;为每个容器分配、配置IP等&#xff0c;并将容器连接到一个docker0。使用–network bridge命令指定&#xff0c;…

【每日OJ —— 622. 设计循环队列】

每日OJ —— 622. 设计循环队列 1.题目&#xff1a;622. 设计循环队列2.解法2.1.解法讲解2.1.1.算法讲解2.1.2.代码实现2.1.3.提交通过展示 1.题目&#xff1a;622. 设计循环队列 2.解法 1.本题有很多解法&#xff1a;可以使用数组&#xff0c;单链表&#xff0c;双链表&#x…

控制论与科学方法论

《控制论与科学方法论》&#xff0c;真心推荐。 书籍原文电子版PDF&#xff1a;https://pan.quark.cn/s/aa40d59295df&#xff08;分类在学习目录下&#xff09; 备用链接&#xff1a;https://pan.xunlei.com/s/VNgj2vjW-Hf_543R2K8kbaifA1?pwd2sap# 控制论是一种让系统按照我…

【精选】CSS入门必看知识点大合集

CSS简介 CSS概念 CSS&#xff08;Cascading Style Sheets&#xff09;层叠样式表&#xff0c;又叫级联样式表&#xff0c;简称样式表 CSS文件后缀名为.css CSS用于HTML文档中元素样式的定义 为什么需要CSS 使用css的唯一目的就是让网页具有美观一致的页面 语法 CSS 规则…

每日一题(LeetCode)----链表--分隔链表

每日一题(LeetCode)----链表–分隔链表 1.题目&#xff08;86. 分隔链表&#xff09; 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初…

Vue3使用dataV报错问题解决

DataV官网&#xff1a;https://datav-vue3.jiaminghi.com/guide/ vue2中是没有问题的&#xff0c;这是第一次在vue3中使用发现的报错问题 报错问题 首先安装&#xff1a; pnpm add dataview/datav-vue3 1. 全局注册报错 然后main.ts全局注册 import { createApp } f…

Redis-Day1基础篇(初识Redis, Redis常见命令, Redis的Java客户端)

Redis-Day1基础篇 初识Redis认识NoSQL认识Redis安装Redis启动RedisRedis客户端 Redis命令数据结构介绍通用命令操作命令StringHashListSetSortedSet Redis的Java客户端客户端对比Jedis客户端Jedis快速入门Jedis连接池 SpringDataRedis客户端SpringDataRedis概述SpringDataRedis…

浅谈WPF之各种Template

前几天写了一篇文章【浅谈WPF之控件模板和数据模板】&#xff0c;有粉丝反馈说这两种模板容易弄混&#xff0c;不知道什么时候该用控件模块&#xff0c;什么时候该用数据模板&#xff0c;以及template和itemtemplate之间的关系等&#xff0c;今天专门写一篇文章&#xff0c;简述…

推荐一款适合做智慧旅游的前端模板

目录 前言 一、功能介绍 二、前端技术介绍 三、功能及界面设计介绍 1、数据概览 2、车辆监控 3、地图界面 4、其它功能 四、扩展说明 总结 前言 智慧旅游是一种全新的旅游业务模式&#xff0c;它充分利用先进的信息技术&#xff0c;提升旅游体验&#xff0c;优化旅游管…

【HarmonyOS】元服务卡片本地启动拉起加桌没问题,上架后拉起加桌时卡片展示异常

【关键字】 加桌选卡展示异常 、 2卡共用一个布局 、 代码混淆 【问题现象】 元服务卡片在本地启动拉起加桌时&#xff0c;多卡的选卡过程显示是没问题的。但是在上架后拉起加桌时&#xff0c;多卡的选卡过程卡片展示异常。 代码逻辑是通过创建卡片的时候判断卡片的尺寸大小…

设计模式-16-Spring源码中的设计模式

1-Spring之观察者模式 Java、Google Guava都提供了观察者模式的实现框架。Java提供的框架比较简单&#xff0c;只包含java.util.Observable和java.util.Observer两个类。Google Guava提供的框架功能比较完善和强大&#xff1a;通过EventBus事件总线来实现观察者模式。实际上&am…

对比两个数组中对应位置的两个元素将每次对比的最大值用于构成新的数组np.maximum()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 对比两个数组中对应位置的两个元素 将每次对比的最大值用于构成新的数组 np.maximum() 选择题 以下代码的输出结果为&#xff1f; import numpy as np a1 [1,2,33] a2 [11,2,3] print("…