数据结构与算法——19.红黑树
这篇文章我们来讲一下红黑树。
目录
1.概述
1.1红黑树的性质
2.红黑树的实现
3.总结
1.概述
首先,我们来大致了解一下什么是红黑树
红黑树是一种自平衡的二叉查找树,是一种高效的查找树。红黑树具有良好的效率,它可在 O(logN) 时间内完成查找、增加、删除等操作。因此,红黑树在业界应用很广泛,比如 Java 中的 TreeMap,JDK 1.8 中的 HashMap。
1.1红黑树的性质
看过前面二叉查找树(即二叉搜索树)的同学都知道,普通的二叉查找树在极端情况下可退化成链表,此时的增删查效率都会比较低下。
为了避免这种情况,就出现了一些自平衡的查找树,比如 AVL,红黑树等。这些自平衡的查找树通过定义一些性质,将任意节点的左右子树高度差控制在规定范围内,以达到平衡状态。前面讲的AVL树实现自平衡的机制是设置一个平衡因子。
以红黑树为例,红黑树通过如下的性质定义实现自平衡:
- 所有节点都有两种颜色:红色或黑色。
- 根节点是黑色。
- 所有null视为黑色。
- 每个红色节点必须有两个黑色的子节点,即红色节点不能相邻(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
- 从根节点到任意一个叶子节点,路径中的黑色节点数目一样(黑色完美平衡,简称黑高)。
有了上面的几个性质作为限制,即可避免二叉查找树退化成单链表的情况。
但是,仅仅避免这种情况还不够,这里还要考虑某个节点到其每个叶子节点路径长度的问题。
如果某些路径长度过长,那么,在对这些路径上的节点进行增删查操作时,效率也会大大降低。
这个时候性质4和性质5用途就凸显了,有了这两个性质作为约束,即可保证任意节点到其每个叶子节点路径最长不会超过最短路径的2倍。原因如下:
当某条路径最短时,这条路径必然都是由黑色节点构成。当某条路径长度最长时,这条路径必然是由红色和黑色节点相间构成(性质4限定了不能出现两个连续的红色节点)。而性质5又限定了从任一节点到其每个叶子节点的所有路径必须包含相同数量的黑色节点。此时,在路径最长的情况下,路径上红色节点数量 = 黑色节点数量。该路径长度为两倍黑色节点数量,也就是最短路径长度的2倍。
下面看几个红黑树:
2.红黑树的实现
下面来看一下红黑树的实现
package Tree;
/**红黑树的相关操作*/
public class L4_RBTree {enum Color{RED,BLACK}private Node root;private static class Node {int key;Object value;Node left;Node right;Node parent;//父节点Color color = Color.RED;//颜色public Node(int key, Object value) {this.key = key;this.value = value;}//是否是左孩子boolean isLeftChild(){return parent != null && parent.left == this;}//找叔叔结点Node isUncle(){if (parent == null || parent.parent == null){return null;}if (parent.isLeftChild()){return parent.parent.right;}else {return parent.parent.left;}}//找兄弟Node sibling(){if (parent == null){return null;}if (this.isLeftChild()){return parent.right;}else {return parent.left;}}}//判断是不是红色boolean isRed(Node node){return node != null && node.color == Color.RED;}//判断是不是黑色boolean isBlack(Node node){return !isRed(node);}/*** 右旋* 要对parent进行处理* 选转后新根的父子关系* */private void rightRotate(Node node){Node nodeParent = node.parent;Node nodeLeft = node.left;Node nodeLeftRight = nodeLeft.right;if (nodeLeftRight != null){nodeLeftRight.parent = node;}nodeLeft.right = node;nodeLeft.parent = nodeParent;node.left = nodeLeftRight;node.parent = nodeLeft;if (nodeParent == null){root = nodeLeft;}else if (nodeParent.left == node){nodeParent.left = nodeLeft;}else {nodeParent.right = nodeLeft;}}//左旋private void leftRotate(Node node){Node nodeParent = node.parent;Node nodeRight = node.right;Node nodeRightLeft = nodeRight.left;if (nodeRightLeft != null){nodeRightLeft.parent = node;}nodeRight.left = node;nodeRight.parent = nodeParent;node.right = nodeRightLeft;node.parent = nodeRight;if (nodeParent == null){root = nodeRight;}else if (nodeParent.left == node){nodeParent.left = nodeRight;}else {nodeParent.right = nodeRight;}}/**新增或更新*/public void put(int key,Object value){Node p = root;Node parent = null;while (p != null){if (key < p.key){p = p.left;} else if(p.key < key){p = p.right;} else {p.value = value;return;}}Node inserted = new Node(key,value);if (parent == null){root = inserted;}else if (key < parent.key){parent.left = inserted;inserted.parent = parent;}else {parent.right = inserted;inserted.parent = parent;}fixRedRed(inserted);}/**节点调整*/private void fixRedRed(Node x){//插入节点是跟节点if (x == root){x.color = Color.BLACK;return;}//插入节点的父亲是黑色if (isBlack(x.parent)){return;}//红红相邻且叔叔为红Node parent = x.parent;Node uncle = x.isUncle();Node grandparent = parent.parent;if (isRed(uncle)){parent.color = Color.BLACK;uncle.color = Color.BLACK;grandparent.color = Color.RED;fixRedRed(grandparent);return;}//红红相邻且叔叔为黑if(parent.isLeftChild() && x.isLeftChild()){//LLparent.color = Color.BLACK;grandparent.color = Color.RED;rightRotate(grandparent);}else if (parent.isLeftChild() && !x.isLeftChild()){//LRleftRotate(parent);x.color = Color.BLACK;grandparent.color = Color.RED;rightRotate(grandparent);} else if (!parent.isLeftChild() && !x.isLeftChild()){//RRparent.color = Color.BLACK;grandparent.color = Color.RED;leftRotate(grandparent);}else {//RLrightRotate(parent);x.color = Color.BLACK;grandparent.color = Color.RED;leftRotate(grandparent);}}/**删除*/public void remove(int key){Node deleted = find(key);if (deleted == null){return;}doRemove(deleted);}private void doRemove(Node deleted){Node replaced = findReplaced(deleted);Node parent = deleted.parent;if (replaced == null){//没有孩子//删除的是根节点if (deleted == root){root = null;}else {if(isBlack(deleted)){//复杂调整fixDoubleBlack(deleted);}else {// 无需处理}if (deleted.isLeftChild()){parent.left = null;}else {parent.right = null;}deleted.parent = null;}return;}//有一个孩子if (deleted.left == null || deleted.right == null){//删除的是根节点if (deleted == null){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 (isBlack(deleted) && isBlack(replaced)){//复杂处理fixDoubleBlack(replaced);}else {replaced.color = Color.BLACK;}}return;}//有两个孩子(是一种转换的操作)int t = deleted.key;deleted.key = replaced.key;replaced.key = t;Object v = deleted.value;deleted.value = replaced.value;replaced.value = v;doRemove(replaced);}/**遇到双黑的不平衡的复杂处理* x表示待调整的结点* */private void fixDoubleBlack(Node x){if (x == root){return;}Node parent = x.parent;Node sibling = x.sibling();//被调整节点的兄弟节点为红色if (isRed(sibling)){if (x.isLeftChild()){leftRotate(parent);}else {rightRotate(parent);}parent.color = Color.RED;sibling.color = Color.BLACK;fixDoubleBlack(x);return;}//兄弟是黑色且两个侄子都是黑色if (sibling != null){if (isBlack(sibling.left) && isBlack(sibling.right)){sibling.color = Color.RED;if (isRed(parent)){parent.color = Color.BLACK;}else {fixDoubleBlack(parent);}}//兄弟是黑色但是侄子有红色else {//LLif(sibling.isLeftChild() && isRed(sibling.left)){rightRotate(parent);sibling.left.color = Color.BLACK;sibling.color = parent.color;parent.color = Color.BLACK;}//LRelse if (sibling.isLeftChild() && isRed(sibling.right)){sibling.right.color = parent.color;leftRotate(sibling);rightRotate(parent);parent.color = Color.BLACK;}//RLelse if (!sibling.isLeftChild() && isRed(sibling.left)){sibling.left.color = parent.color;rightRotate(sibling);leftRotate(parent);parent.color = Color.BLACK;}//RRelse {leftRotate(parent);sibling.right.color = Color.BLACK;sibling.color = parent.color;parent.color = Color.BLACK;}}}else {fixDoubleBlack(parent);}}/**找到要删除的结点*/private Node find(int key){Node p = root;while (p != null){if (key < p.key){p = p.left;}else if (key > p.key){p = p.right;}else {return p;}}return null;}/**查找剩余结点*/private Node findReplaced(Node deleted){if (deleted.left == null && deleted.right == null){return null;}else if (deleted.left == null){return deleted.right;}else if(deleted.right == null){return deleted.left;}Node s = deleted.right;while (s.left != null){s = s.left;}return s;}
}
3.总结
有一说一,这红黑树确实比前面的几种树要难一点,主要是它的属性太多,限制太多。说实话,这篇文章中仅仅只是简单的介绍了一下红黑树,实现了一下红黑树的相关操作,但是红黑树的增加和删除中的一些操作没有细讲,这个有时间了我后面会单独再出一篇红黑树的补充文章然后细讲,这篇就这样了吧。
最后说一点,对于这种类似于链式的结构(实际是树形结构),我们要掌握它的定义,条件,然后画图,然后对照图来进行相关的操作,然后再用代码去实现,这样好一点,而不是凭空想象着去写,那样是写不出来的。
相关文章:
数据结构与算法——19.红黑树
这篇文章我们来讲一下红黑树。 目录 1.概述 1.1红黑树的性质 2.红黑树的实现 3.总结 1.概述 首先,我们来大致了解一下什么是红黑树 红黑树是一种自平衡的二叉查找树,是一种高效的查找树。红黑树具有良好的效率,它可在 O(logN) 时间内完…...
js题解(三)
文章目录 柯里化模块乘法改变上下文 柯里化 已知 fn 为一个预定义函数,实现函数 curryIt,调用之后满足如下条件: 1、返回一个函数 a,a 的 length 属性值为 1(即显式声明 a 接收一个参数) 2、调用 a 之后&a…...
CompletableFuture异步回调
CompletableFuture异步回调 CompletableFutureFuture模式CompletableFuture详解1.CompletableFuture的UML类关系2.CompletionStage接口3.使用runAsync和supplyAcync创建子任务4.设置子任务回调钩子5.调用handle()方法统一处理异常和结果6.线程池的使用 异步任务的串行执行thenA…...
Python中匹配模糊的字符串
嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 如何使用thefuzz 库,它允许我们在python中进行模糊字符串匹配。 此外,我们将学习如何使用process 模块,该模块允许我们在模糊…...
PHP图片文件管理功能系统源码
文件图库管理单PHP源码直接解压就能用,单文件,indexm.php文件可以重新命名,上传到需要访问的目录中, 可以查看目录以及各个文件,图片等和下载及修改管理服务。 源码下载:https://download.csdn.net/downloa…...
(枚举 + 树上倍增)Codeforces Round 900 (Div. 3) G
Problem - G - Codeforces 题意: 思路: 首先,目标值和结点权值是直接联系的,最值不可能直接贪心,一定是考虑去枚举一些东西,依靠这种枚举可以遍历所有的有效情况,思考的方向一定是枚举 如果去…...
websocket逆向【python实现websocket拦截】
python实现websocket拦截 前言一、拦截的优缺点优点:缺点:二、实现方法1.环境配置2.代码三、总结前言 开发者工具F12,筛选ws后,websocket的消息是这样显示的,如何获取这里面的消息呢? 以下是本篇文章正文内容 一、拦截的优缺点 主要讲解一下websocket拦截的实现,现在…...
软件测试自动化的成本效益分析
随着软件测试技术的发展,人们已经从最初的手工测试转变为手工和自动化技术相结合的测试方法。目前,人们更多的是关心自动化测试框架、自动化测试工具以及脚本研究等技术方面,而在软件自动化测试方案的效益分析方面涉及较少。 软件测试的目的是…...
【Java】状态修饰符 final static
目录 final 修饰我们的成员方法、成员变量、类 示例代码: final 修饰的局部变量 示例代码: static 示例代码: static 访问特点: 示例代码: static关键字的用途 示例代码: static 修饰常量 示例…...
笔试编程ACM模式JS(V8)、JS(Node)框架、输入输出初始化处理、常用方法、技巧
目录 考试注意事项 先审完题意,再动手 在本地编辑器(有提示) 简单题515min 通过率0%,有额外log 常见输入处理 str-> num arr:line.split( ).map(val>Number(val)) 初始化数组 new Array(length).fill(v…...
learn掩码张量
目录 1、什么是掩码张量 2、掩码张量的作用 3、代码演示 (1)、定义一个上三角矩阵,k0或者 k默认为 0 (2)、k1 (3)、k-1 4、掩码张量代码实现 (1)、输出效果 &…...
激活函数介绍
介绍 神经网络当中的激活函数用来提升网络的非线性,以增强网络的表征能力。它有这样几个特点:有界,必须为非常数,单调递增且连续可求导。我们常用的有sigmoid或者tanh,但我们都知道这两个都存在一定的缺点,…...
docker方式启动一个java项目-Nginx本地有代码,并配置反向代理
文章目录 案例导入说明1.安装MySQL1.1.准备目录1.2.运行命令1.3.修改配置1.4.重启 2.导入SQL3.导入Demo工程3.1.分页查询商品(仔细看代码,很多新的MP编程技巧)3.2.新增商品3.3.修改商品3.4.修改库存3.5.删除商品3.6.根据id查询商品3.7.根据id…...
前端和后端是Web开发选哪个好?
前端和后端是Web开发中的两个不同的领域,哪一种更适合学习?前景更广呢? 一、引言 Web前端开发就像装饰房间的小瓦匠,勤勤恳恳,仔仔细细,粉饰墙壁,妆点家具。会 HTML,CSS,懂点 JS。…...
HTTP协议,请求响应
、概述 二、HTTP请求协议 三、HTTP响应协议 四、请求数据 1.简单实体参数 RequestMapping("/simpleParam")public String simpleParam(RequestParam(name "name" ,required false ) String username, Integer age){System.out.println (username "…...
idea配置文件属性提示消息解决方案
在项目文件路径下找到你没有属性提示消息的文件 选中,ok即可 如果遇到ok无法确认的情况: 在下图所示位置填写配置文件名称即可...
EdgeView 4 for Mac:重新定义您的图像查看体验
您是否厌倦了那些功能繁杂、操作复杂的图像查看器?您是否渴望一款简单、快速且高效的工具,以便更轻松地浏览和管理您的图像库?如果答案是肯定的,那么EdgeView 4 for Mac将是您的理想之选! EdgeView 4是一款专为Mac用户…...
流程自动化(RPA)的好处有哪些?
流程自动化(RPA)是一种通过软件机器人实现业务流程自动化的技术。它可以模拟人类在计算机上执行的操作,从而自动化重复性、繁琐的任务,提高工作效率和准确性。流程自动化(RPA)的好处很多,下面我…...
医学影像系统【简称PACS】源码
PACS(Picture Archiving and Comuniations Systems)即PACS,图像存储与传输系统,是应用于医院中管理医疗设备如CT,MR等产生的医学图像的信息系统。目标是支持在医院内部所有关于图像的活动,集成了医疗设备,图像存储和分…...
大家都在用哪些敏捷开发项目管理软件?
敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。 敏捷开发的特点是高度灵活性和适应性、迭代式开发。 敏捷开发方法强调快速响应变化,因此它具有高度的灵活性和适应性。开发团队可以根据客户需求和市场变化快速调整开发计划和产品功能,以确保产品…...
python机器学习基础教程01-环境搭建
书籍源代码 github上源代码 https://github.com/amueller/introduction_to_ml_with_python 安装anaconda虚拟环境 创建虚拟环境 conda create -p E:\Python\envs\mlstupy35 python3.5 # 激活环境 conda activate E:\Python\envs\mlstupy35 # 创建学习目录 cd G:\Python\ml…...
TinyWebServer学习笔记-Config
为了弄清楚具体的业务逻辑,我们直接从主函数开始看源代码: #include "config.h"int main(int argc, char *argv[]) {//需要修改的数据库信息,登录名,密码,库名string user "root";string passwd "root";string databas…...
数据结构与算法--算法
这里写目录标题 线性表顺序表链表插入删除算法 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 线性表 顺序表 链表 插入删除算法 步骤 1.通过循环到达指定位置的前一个位置 2.新建…...
JVM:如何通俗的理解并发的可达性分析
并发的可达性分析 前面在介绍对象是否已死那一节有说到可达性分析算法,它理论上是要求全过程都基于一个能保障一致性的快照(类比 MySQL 的MVCC)中才能够进行分析,也就意味着必须全程冻结用户线程的运行(STW࿰…...
传统机器学习聚类算法——总集篇
工作需要,涉及到一些聚类算法相关的知识。工作中需要综合考虑数据量、算法效果、性能之间的平衡,所以开启新的篇章——机器学习聚类算法篇。 传统机器学习中聚类算法主要分为以下几类: 1. 层次聚类算法 层次聚类算法是一种无监督学习算法&am…...
Ajax
一、什么是Ajax <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…...
SQL_ERROR_INFO: “Duplicate entry ‘9003‘ for key ‘examination_info.exam_id‘“
今天刷题的时候,往数据库中插入一条语句,但是这个语句已经存在于数据库中了,所以不能用insert into 语句来插入,应该使用replace into 来插入。 REPLACE INTO examination_info(exam_id,tag,difficulty,duration,release_time) V…...
解决每次重启ganache虚拟环境,十个账号秘钥都会改变问题
很多时候 我们启动一个 ganache 环境 然后 通过私钥 在 MetaMask 中 导入用户 但是 当我们因为 电脑要关机呀 或者 ETH 消耗没了呀 那我们就不得不重启一个ganache虚拟环境 然后 你在切一下网络 让它刷新一下 你就会发现 上一次导入的用户就没有了 这是因为 你每次 ganache…...
sheng的学习笔记-【中文】【吴恩达课后测验】Course 2 - 改善深层神经网络 - 第一周测验
课程2_第1周_测验题 目录:目录 第一题 1.如果你有10,000,000个例子,你会如何划分训练/验证/测试集? A. 【 】33%训练,33%验证,33%测试 B. 【 】60%训练,20%验证,20%测试 C. 【 】98…...
(粗糙的笔记)动态规划
动态规划算法框架: 问题结构分析递推关系建立自底向上计算最优方案追踪 背包问题 输入: n n n个商品组成的集合 O O O,每个商品有两个属性 v i v_i vi和 p i p_i pi,分别表示体积和价格背包容量 C C C 输出: …...
优化志愿网站/网络精准推广
作为网页设计师或前端工程师,你可能还记得曾经的那个网页大小建议:一个网页(包括HTML、CSS、Javacript、Flash和图片)尽量不要超过30KB的大小,随着互联网的日益庞大,网络带宽也在飞速发展,很多设计师已经不再考虑这条3…...
濮阳网站建设哪里便宜/百度点击器找名风
Fedora安装Nvidia G105M(使用RPMFusion驱动)2010-9-15日更新 计算机配置: 显卡:Nvidia G105M 网卡: Alheros AR8121/AR8113/AR8114 无线:Intel5100 安装好Fedora12-13(使用PAE内核)以…...
创建网站/网页推广平台
嵌入式软件设计第7次实验报告 学号:140201234 姓名:王凯 组别:第四组 实验地点:D19 一、实验目的: 1.熟悉网线的制作(T568B标准直连线) 2.学会使用HTML语言…...
网站建设公司哪个好/百度一下你知道主页官网
文章与教程 Planning Android Screens (slides.com) Dmytro Danylyk的演讲幻灯片,关于程序员和设计师如何一起设计一款新的app。 我在Droidcon London上做的事情(medium.com) Joe Birch概括了他在Droidcon London上的经历。 掌握Coordinator Layout (saulmm.gith…...
wordpress默认页面设置/网页制作工具
2013年,天文学家发现了一个小型椭圆星系,然而这个椭圆星系一直是个谜。该星系没有任何特征、没有其他星系的螺旋结构,看起来像是一个被孤立的星系,仿佛与宇宙中所有的外层恒星没有任何关联。为解开离群星系之谜,天文学…...
深圳大公司/百度热搜关键词排名优化
倒数60秒价格走势 可是然而但是,别人家都是怎么拍中的?答:不按套路出牌这次你就中了。 那么即使是这样,还有没有“套路”可循?答:肯定有的。 相信很多人都读过国拍官网的《网上投标拍卖操作流程》ÿ…...