Java的二叉树、红黑树、B+树

数组和链表是常用的数据结构,数组虽然查找快(有序数组可以通过二分法查找),但是插入和删除是比较慢的;而链表,插入和删除很快(只需要改变一些引用值),但是查找就很慢,需要从头开始遍历;
那么有没有一种数据结构能同时具备数组查找快的优点以及链表插入和删除快的优点呢,那就是接下来要介绍的——树。

1、二叉查找树

特性:

  • 1、左子树上所有节点的值均小于它的根节点的值;
  • 2、右子树上所有节点的值均大于它的根节点的值;
  • 3、左、右子树也分别为二叉排序树。
    但是一个不好会形成链表结构,比如一个有序数组形成的二叉树

 

2、平衡二叉查找树

特性:

  • 1、在二叉树的基础上,要求两个子树的高度差不能超过1;
  • 2、每次增删都会通过一次或多次旋转来平衡二叉树;

调整平衡的基本思想:

当在二叉排序树中插入一个节点时,首先检查是否因插入而破坏了平衡,若破坏,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子树中节点之间的关系,以达到新的平衡。
所谓最小不平衡子树,指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。

先插入指定节点,记录下当前节点的信息,LH,EH或者RH。

  1. 若左子树高LH,查看其左子树根节点的信息,若是LH,则一次右旋;若是RH,则一次左旋+一次右旋
  2. 若右子树高RH,查看右子树根节点的信息,若是RH,则一次左旋;若是LH,则一次右旋+一次左旋
  3. 调整改变的节点信息

虽然平衡树解决了二叉查找树退化为近似链表的缺点,能够把查找时间控制在 O(logn),不过却不是最佳的,因为平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树,且随着树的高度的增加,动态插入和删除的代价也越来越大,为了解决优化插入和删除性能,红黑树出现了。

显然,如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣,为了解决这个问题,于是有了红黑树,红黑树具有如下特点:

3、红黑树:

特性:

  • 1、根节点是黑色;
  • 2、每个节点或者是黑色,或者是红色;
  • 3、每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!;
  • 4、如果一个节点是红色的,则它的子节点必须是黑色的,也就是说一个红节点的父节点只能是黑色
  • 5、从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点,也就是说确保没有一条路径会比其他路径长出俩倍;

红黑树(Red Black Tree) 是一种自平衡二叉查找树
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
二叉平衡树的严格平衡策略以牺牲建立查找结构(插入,删除操作)的代价,换来了稳定的O(logN) 的查找时间复杂度
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

红黑树的操作代价:

  • 查找代价:由于红黑树的性质(最长路径长度不超过最短路径长度的2倍),可以说明红黑树虽然不像平衡二叉查找树一样是严格平衡的,但平衡性能还是要比二叉查找树要好。其查找代价基本维持在O(logN)左右,但在最差情况下(最长路径是最短路径的2倍少1),比平衡二叉查找树要略逊色一点。

  • 插入代价:RBT插入结点时,需要旋转操作和变色操作。但由于只需要保证RBT基本平衡就可以了。因此插入结点最多只需要2次旋转,这一点和平衡二叉查找树的插入操作一样。虽然变色操作需要O(logN),但是变色操作十分简单,代价很小。

  • 删除代价:红黑树的删除操作代价要比平衡二叉查找树要好的多,删除一个结点最多只需要3次旋转操作。

RBT 效率总结 : 查找效率最好情况下时间复杂度为O(logN),但在最坏情况下比平衡二叉查找树要差一些,但也远远好于二叉查找树。
插入和删除操作改变树的平衡性的概率要远远小于AVL(RBT不是高度平衡的)。因此需要的旋转操作的可能性要小,而且一旦需要旋转,插入一个结点最多只需要旋转2次,删除最多只需要旋转3次(小于AVL的删除操作所需要的旋转次数)。虽然变色操作的时间复杂度在O(logN),但是实际上,这种操作由于简单所需要的代价很小。

红黑树能够以O(log2(N))的时间复杂度进行搜索、插入、删除操作。此外,任何不平衡都会在3次旋转之内解决。这一点是平衡二叉查找树所不具备的。

调整平衡的基本思想:

  • 变色:为了重新符合红黑树的规则,尝试把红色节点变为黑色,或者把黑色节点变为红色。
  • 左旋转:逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。说起来很怪异,大家看下图:

  • 右旋转:顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。大家看下图:

场景:HashMap TreeSet TreeMap

4、 B树:

设计缘由:

数据库索引是存储在磁盘上的,当数据量比较大的时候,索引的大小可能有十几个G,甚至更多。当我们利用索引查询的时候,能把整个索引全部加载到内存吗?显然不可能,能做的只有逐一加载每一个磁盘页,这里的磁盘页对应着索引树的节点。

如果我们利用二叉查找树作为索引结构,那么最坏(每个节点数据在不同磁盘页上)的情况下,磁盘IO次数等于索引树的高度。 所以,为了减少磁盘IO次数,我们就需要把原本“瘦高”的树结构变得“矮胖”(节点数变少了)。这就是B树的特征之一

B树是一种多路平衡查找树,它的每一个节点最多包含m个孩子,m被称为B树的阶,m的大小取决于磁盘页的大小。——一个节点最多包含一个磁盘页的数据

也就是说,当B树变得矮胖以后,每个节点可以包含多个数据(数据量大小由磁盘页的大小决定),同样的数据,B树比二叉树/红黑树节点少。所以,B树在查询时,磁盘IO次数变少了,从而可以提升查找性能。

特征:

一个m阶的B树具有如下几个特征:

  • 1.根结点至少有两个子女。
  • 2.每个中间节点都包含k-1个元素和k个孩子,其中 m/2 <= k <= m
  • 3.每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m
  • 4.所有的叶子结点都位于同一层。
  • 5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划

场景:

B树主要应用于文件系统以及部分数据库索引,比如著名的非关系型数据MongoDB。
而大部分关系型数据库,比如mysql,则使用B+树作为索引

5、 B+树

B+树是B树的一种变体,但是又有所不同,

特征:

一个m阶的B+树具有如下几个特征:

  • 1.有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。

  • 2.所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

  • 3.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

这里特别要注意有两点:
其一:每个父节点的元素都出现在子节点中,是子节点的最大(或最小)元素;因此所有叶子节点包含了全量元素信息;
其二:每个叶子节点都带有指向下一个节点的指针,形成了一个有序链表;
其三:只有叶子节点带有卫星数据,其余中间节点仅仅是索引,没有任何数据关联,如下图,所谓卫星数据,指的是索引元素所指向的数据记录,比如数据库中的某一行,在B树种,无论中间节点还是叶子节点都带有卫星数据。

设计优势

B+树的好处主要体现在查询性能上,由于B+树的中间节点没有卫星数据,所以同样大小的磁盘页可以容纳更多的节点元素,这就意味着,一次性加载到内存中的节点元素更多,从而使得查询时IO次数也更少。(举个简单的例子,一个磁盘页可以加载B树的100个节点元素,但是可以加载B+树的1000个节点元素,那么对于查找999这个数来说,B数需要10次IO,B+数只需要1次IO)

B+树相对B树的优点:

  • IO一次读数据是从磁盘上读的,磁盘容量是固定的,取数据量大小是固定的,非叶子节点不存储数据,节点小,磁盘IO次数就少。

  • B+树查询性能稳定,因为B+树的查询必须最终查找到叶子节点;
    而B树,只要找到匹配元素即可,无论匹配元素处于中间节点,还是叶子节点。所以B树的查询性能并不稳定,最好情况是只查根节点,最坏情况是查到叶子节点

  • B+树的所有Data域在叶子节点,一般来说都会进行一个优化,就是将所有的叶子节点用指针串联起来(可以认为是链表),遍历叶子节点就能获取全部数据,这样就能进行区间访问了。
    B树做范围查询,只能繁琐的遍历,但是B+树,只需要查到查找到范围下限以后,遍历叶子节点(有序链表)就可以了。

综合起来,B+树比B树的优势有三个:1、IO次数更少;2、查询性能更佳;3、范围查询简便

场景:Mysql索引

6、红黑树 VS B+树

红黑树的深度比B+树大,当数据量小时,可以把数据完全放到内存中,红黑树的时间复杂度比B树低(不用每次都查到叶子节点),如linux中进程的调度用的是红黑树,Java中HashMap、TreeMap、TreeSet(都在内存中操作)也都是用红黑树实现;

但是,当数据量大时,不能一次性把数据放到内存时,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下;而B树因其读磁盘次数少,具有更快的速度。

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

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

相关文章

【C#】组件化开发,调用dll组件方法

系列文章 C#项目–业务单据号生成器&#xff08;定义规则、自动编号、流水号&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129129787 C#项目–开始日期结束日期范围计算&#xff08;上周、本周、明年、前年等&#xff09; 本文链接&…

快排函数 -- qsort函数(Quick Sort)

文章目录&#x1f50e;1.qsort函数简介&#x1f4a1;1.1.函数原型&#x1f4a1;1.2.参数含义&#x1f50e;2.比较函数介绍&#x1f50e;3.比较函数使用案例&#x1f4a1;3.1.整型数组&#x1f4a1;3.2.浮点型数组&#x1f4a1;3.3.结构体类型 - 字符串&#x1f50e;4.利用冒泡排…

震撼,支持多模态模型的ChatGPT 4.0发布了

最近几个月&#xff0c;互联网和科技圈几乎ChatGPT刷屏了&#xff0c;各种关于ChatGPT的概念和应用的帖子也是围绕在周围。当去年年底ChatGPT发布的那几天&#xff0c;ChatGPT确实震撼到了所有人&#xff0c;原来AI还可以这么玩&#xff0c;并且对国内的那些所谓的人工智能公司…

Tesla都使用什么编程语言?

作者 | 初光 出品 | 车端 备注 | 转载请阅读文中版权声明 知圈 | 进“汽车电子与AutoSAR开发”群&#xff0c;请加微“cloud2sunshine” 总目录链接>> AutoSAR入门和实战系列总目录 带着对更美好未来的愿景&#xff0c;特斯拉不仅成为有史以来最有价值的汽车公司&…

多线程(初阶)

文章目录一.初始线程(Thread)1.1.线程的概念1.2.线程的优势1.2.1.线程比进程更轻量1.2.2.并发编程1.3.线程和进程的区别二.Thread类方法2.1. java 中创建线程的方法2.1.1. 继承Thread,重写run2.1.2. 实现Ruuable接口2.1.3. 使用匿名内部类,继承Thread2.1.4.使用匿名内部类,实现…

[蓝桥杯单片机]——八到十一届初赛决赛客观题

第八届初赛 一、填空题 采用外部12MHz晶振&#xff0c;经过系统12分频时定时器获得最大定时长度&#xff0c;此时定时器定时脉冲为1MHz&#xff0c;周期为1s&#xff0c;而定时器计时均为16位加法计数器&#xff0c;即计时长度为。 二、 选择题 ①带阻滤波器是指能通过大多数频…

处理窄区路径规划的业务问题

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、通过栅格地图的处理解决二、使用bug绕障的方式走出窄区&#xff0c;或者结合边界图形参考bug算法沿边出来三、使用维诺图计…

字符串函数和内存函数

&#x1f355;博客主页&#xff1a;️自信不孤单 &#x1f36c;文章专栏&#xff1a;C语言 &#x1f35a;代码仓库&#xff1a;破浪晓梦 &#x1f36d;欢迎关注&#xff1a;欢迎大家点赞收藏关注 字符串函数和内存函数 文章目录字符串函数和内存函数前言1. 字符串函数介绍1.1 s…

【MySQL】MySQL的优化(一)

目录 查看SQL执行频率 定位低效率执行SQL 定位低效率执行SQL-慢查询日志 定位低效率执行SQL-show processlist 查看SQL执行频率 MySQL 客户端连接成功后&#xff0c;通过 show [session|global] status 命令可以查看服务器状态信息。通 过查看状态信息可以查看对当…

jvm类与类加载

1.类加载过程&#xff1a; 首先要加载某个类一定是出于某种目的&#xff0c;比如要运行java程序&#xff0c;那么久必须加载主类才能运行其中的方法&#xff0c;所以一般在这些情况下&#xff0c;如果类没有被加载&#xff0c;就会自动被加载&#xff1a; 1.使用new创建对象时 …

MyBatis开发环境搭建

1.创建工程 2.引入相关的依赖 pom.xml <dependencies><!--1.引入mybatis包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.6</version></dependency><!--2.单元…

FPGA和IC设计怎么选?哪个发展更好?

很多人纠结FPGA和IC设计怎么选&#xff0c;其实往小了说&#xff0c;要看你选择的具体是哪个方向岗位。往大了说&#xff0c;将来你要是走更远&#xff0c;要成为大佬&#xff0c;那基本各个方向的都要有涉及的。 不同方向就有不同的发展&#xff0c;目前在薪资上IC设计要比FP…

Qt之高仿QQ系统设置界面

QQ或360安全卫士的设置界面都是非常有特点的,所有的配置项都在一个垂直的ScrollArea中,但是又能通过左侧的导航栏点击定位。这样做的好处是既方便查看指定配置项,又方便查看所有配置项。 一.效果 下面左边是当前最新版QQ的系统设置界面,右边是我的高仿版本,几乎一毛一样…

【Linux】进程的程序替换

文章目录1. 程序替换1.创建子进程的目的是什么&#xff1f;2.了解程序是如何进行替换的3. 程序替换的基本原理当创建进程的时候&#xff0c;先有进程数据结构&#xff0c;还是先加载代码和数据&#xff1f;程序替换是整体替换&#xff0c;不是局部替换execl 返回值4. 替换函数1…

【三维几何学习】从零开始网格上的深度学习-2:卷积网络CNN篇(Pytorch)

本文参加新星计划人工智能(Pytorch)赛道&#xff1a;https://bbs.csdn.net/topics/613989052 从零开始网格上的深度学习-2:卷积网络CNN篇引言一、概述1.1 卷积操作简述1.2 网格上的面卷积二、核心代码2.1 面卷积2.2 网络框架三、基于CNN的网格分类3.1 分类结果3.2 全部代码引言…

FPGA之时钟规划图解

目录 一、前言 二、时钟规划概念 三、时钟规划的模块 四、时钟规划之时钟单元布局 4.1 BUFG 4.2 BUFH 4.3 BUFR 4.4 BUFIO 五、时钟规划之时钟单元走线 5.1 BUFG->BUFH 5.2 BUFR->FF 5.3 BUFIO->FF 一、前言 对于vivado这类使用verilog语言的进…

《Netty》从零开始学netty源码(七)之NioEventLoop.selectStrategy

NioEventLoop是一个事件轮询器&#xff0c;在它的run方法中其实是一个for死循环&#xff0c;不断重复三个过程&#xff1a;1. 获取IO事件&#xff0c;2. 处理IO事件&#xff0c;3. 处理任务队列中的task&#xff0c;而SelectStractegy就是用于第一步获取IO事件&#xff0c;它的…

css:使用filter和backdrop-filter实现高斯模糊效果

背景 今天接到一个需求是&#xff0c;使用高斯模糊的效果对一个页面进行模糊处理&#xff0c;正好借这个机会来整理一下 css3 中高斯模糊的两个 API API介绍 filter 说明&#xff1a; 该 API 是一个过滤器&#xff0c;不仅能实现高斯模糊&#xff0c;还有很多比如颜色偏移、…

接口文档包含哪些内容?怎么才能写好接口文档?十年测试老司机来告诉你

目录 接口文档结构 参数说明 示例 错误码说明 语言基调通俗易懂 及时更新与维护 总结 那么我们该如何写好一份优秀的接口文档呢&#xff1f; 接口文档结构 首先我们要知道文档结构是什么样子的。接口文档应该有清晰明确的结构&#xff0c;以便开发人员能快速定位自己需…

经典文献阅读之--Dynamic-VINS(动态点滤除VINS)

0. 简介 现在的SLAM算法在静态环境中表现良好&#xff0c;但在动态环境中很容易失败。最近的工作将基于深度学习的语义信息引入到SLAM系统以减轻动态对象的影响。然而&#xff0c;在资源受限的机器人的动态环境中应用鲁棒定位仍然具有挑战性。所以《RGB-D Inertial Odometry f…