树的知识框架结构如下图所示:

平衡二叉搜索树

平衡二叉搜索树(Balanced Binary Search Tree),英文简称 BBST。经典常见的平衡二叉搜索树是 AVL 树和红黑树。

①二叉搜索树

二叉搜索树(Binary Search Tree)是二叉树的一种,英文简称 BST。又称为二叉查找树、二叉排序树。

它的特点是任何一个结点的值都大于其左子树的所有结点的值,任何一个结点的值都小于其右子树的所有结点的值。

②平衡

平衡(Balance):就是当结点数量固定时,左右子树的高度越接近,这棵二叉树越平衡(高度越低)。而最理想的平衡就是完全二叉树/满二叉树,高度最小的二叉树。

一棵二叉搜索树平均时间复杂度可以认为是树的高度 O(h)。像左边这棵,结点的左右子树的高度接近,属于一棵平衡二叉搜索树,O(h) = O(logn);而右边这棵,高度达到了最大,已经退化成了链表,O(h)=O(n)。

③改进二叉搜索树

当二叉树退化成链表时,性能是很低的,所以我们需要在结点的插入、删除操作之后,想办法让二叉搜索树恢复平衡(减小树的高度)。

但是如果为了追求最理想的平衡,而增加了时间复杂度也不是很有必要,因此比较合理的方案就是:用尽量少的调整次数达到适度平衡。

由此引申出 AVL 树的概念。

AVL 树

AVL 树是最早发明的自平衡二叉搜索树之一,它取名自两位发明家的名字:G.M.Adelson-Velsky 和 E.M.Landis。

平衡因子(Balance Factor):某结点的左右子树的高度差。

每个叶子结点的平衡因子都是 0。看这棵二叉搜索树,红色数字标注了每个结点对应的平衡因子。

举例:8 的左子树高度为 2,右子树高度为 1,因此它的平衡因子为 1;5 的左子树高度为 0,右子树高度为 3,因此它的平衡因子为 -3;4 的左子树高度为 2,右子树高度为 4,因此它的平衡因子为 -2;

再看这棵 AVL 树和它每个结点对应的平衡因子:

可以看到 AVL 树具有以下特点:每个结点的平衡因子只可能是 -1、0、1(如果绝对值超过 1,则认为是失衡)

每个结点的左右子树高度差不超过 1

搜索、插入、删除的时间复杂度是 O(logn)

B 树

B 树(Balanced Tree)是一种平衡的多路搜索树,多用于文件系统、数据库的实现。

这是一个简单的 3 阶 B 树:

特点如下:1 个结点可以存储超过 2 个元素,可以拥有超过 2 个子结点

拥有二叉搜索树的一些性质

平衡,每个结点的所有子树高度一致

比较矮

①m 阶 B 树的性质(m ≥ 2)

m 阶 B 树指的是一个结点最多拥有 m 个子结点。假设一个结点存储的元素个数为 x,那么如果这个结点是:根结点:1 ≤ x ≤ m - 1

非根结点:┌ m / 2 ┐ - 1 ≤ x ≤ m - 1

如果有子结点,子结点个数为 y = x + 1,那么如果这个结点是:根结点:2 ≤ y ≤ m

非根结点:┌ m / 2 ┐ ≤ y ≤ m

向上取整(Ceiling),指的是取比自己大的最小整数,用数学符号 ┌ ┐ 表示;向下取整(Floor),指的是取比自己小的最大整数,用数学符号 └ ┘ 表示。

比如 m=3,子结点个数 2≤y≤3,这个 B 树可以称为(2,3)树、2-3 树。

比如 m=4,子结点个数 2≤y≤4,这个 B 树可以称为(2,4)树、2-3-4 树。

比如 m=5,子结点个数 3≤y≤4,这个 B 树可以称为(3,5)树、3-4-5 树。

以此类推。

②B 树 VS 二叉搜索树

这是一棵二叉搜索树,通过某些父子结点合并,恰好能与上面的 B 树对应。

我们可以得到结论:B 树和二叉搜索树,在逻辑上是等价的

多代结点合并,可以获得一个超级结点,且 n 代合并的超级结点,最多拥有 (2^n) 个子结点 (至少是 (2^n) 阶 B 树)

红黑树定义和性质

红黑树是一种含有红黑结点并能自平衡的二叉搜索树。

为了保证平衡,红黑树必须满足以下性质:每个结点是要么是红色或黑色

根结点必须是黑色

叶结点(外部结点、空结点)是黑色

红色结点不能连续(也就是,红色结点的孩子和父亲都是黑色)

对于每个结点,从该点至 nil(树尾端,Java 中为 null 的结点)的任何路径都包含所相同个数的黑色结点

红黑树与 B 树的等价变换

根据上面的性质,可以画出这样一棵红黑树。接下来对红黑树做等价变换,即将所有的红色结点上升一层与它的父结点放在同一行,这就很像一棵 4 阶 B 树,转换效果如下图所示。

可以得出结论:红黑树与 4 阶 B 树(2-3-4树)具有等价性

黑色结点与红色子结点融合在一起,形成 1 个 B 树结点

红黑树的黑色结点个数与 4 阶 B 树的结点总个数相等

红黑树的基本操作

当我们对一棵平衡二叉搜索树进行插入、删除的时候,很可能会让这棵树变得失衡(最坏可能导致所有祖先结点失衡,但是父结点和非祖先结点都不可能失衡)。

为了达到平衡,需要对树进行旋转。而红黑树能够达到自平衡,靠的也就是左旋、右旋和变色。

旋转操作是局部的。当一侧子树的结点少了,向另一侧“借”一些结点;当一侧子树的结点多了,则“租”一些结点给另一侧。

为了更清楚地讲解这部分内容,先声明几个概念:N-node:当前结点

P-parent:父结点

S-sibling:兄弟结点

U-uncle:叔父结点(P 的兄弟结点)

G-grand:祖父结点(P 的父结点)

左旋

左旋指的是以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。

不考虑结点颜色,可以看到左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树移动。

右旋

右旋指的是以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。

不考虑结点颜色,可以看到右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树移动。

变色

变色指的是结点的颜色由红变黑或由黑变红。

变换规则

将左旋、右旋和变色结合起来,得到一套变换规则。

变色:如果当前结点的父结点和叔父结点是红色,那么:把父结点和叔父结点变为黑色

把祖父结点变为红色

把指针定义到祖父结点

左旋:当前结点是右子树,且父结点是红色,叔父结点是黑色,对它的父结点左旋。

右旋:当前结点是左子树,且父结点是红色,叔父结点是黑色,那么:把父结点变为黑色

把祖父结点变为红色

对祖父结点右旋

红黑树搜索

由于红黑树本来就是平衡二叉搜索树,并且搜索也不会破坏树的平衡,所以搜索算法也与平衡二叉搜索树一致:

具体步骤如下:从根结点开始检索,把根结点设置为当前结点。

若当前结点为空,返回 nil。

若当前结点不为空,比较当前结点 key 与搜索 key 的大小。

若当前结点 key 等于搜索 key,那么该 key 就是搜索目标,返回当前结点。

若当前结点 key 大于搜索 key,把当前结点的左子结点设置为当前结点,重复步骤 2。

若当前结点 key 小于搜索 key,把当前结点的右子结点设置为当前结点,重复步骤 2。

红黑树插入

红黑树插入操作分为下面两步:

定位插入的位置

具体步骤如下:从根结点开始检索。

若根结点为空,那么插入结点设为根结点,结束。

若根结点不为空,那么把根结点设为当前结点。

若当前结点为 nil,返回当前结点的父结点,结束。

若当前结点 key 等于搜索 key,那么该 key 所在结点就是插入结点,更新结点的值,结束。

若当前结点 key 大于搜索 key,把当前结点的左子结点设置为当前结点,重复步骤 4。

若当前结点 key 小于搜索 key,把当前结点的右子结点设置为当前结点,重复步骤 4。

插入后实现自平衡

建议新添加的结点默认为红色,因此这样能够让红黑树的性质尽快满足。不过如果添加的结点是根结点,设为黑色即可。

总结一下红黑树插入可能出现的所有场景:

①场景 1:红黑树为空树

红黑树的性质 2:根结点必须是黑色。

处理:直接把插入结点设成黑色并作为根结点。

②场景 2:插入结点的 key 已存在

二叉搜索树中不能插入相同元素,既然结点的 key 已经存在,红黑树也已平衡,无需重复插入。

处理:将插入结点设为将要替换结点的颜色

更新当前结点的值为插入结点的值

③场景 3:插入结点的父结点为黑色

插入的结点默认是红色的,当它的父结点是黑色时,并不会破坏平衡。

处理:直接插入。

④场景 4:插入结点的父结点为红色

如果插入结点的父结点为红色,那么父结点不可能为根结点,所以插入结点总是存在祖父结点。这点很重要,后续的旋转操作需要祖父结点的参与。

场景 4.1:存在叔父结点,且为红色

由红黑树性质 4 可知:红色结点不能连续。那么此时该插入子树的红黑层数的情况是:黑-红-红。显然最简单的处理方式就是将其改为:红-黑-红。

处理:将父结点和叔父结点变为黑色

将祖父结点变为红色

将祖父结点设置为当前插入结点

场景 4.2:叔父结点不存在或为黑色,插入结点的父结点是祖父结点的左子结点这种场景下,叔父结点所在的子树的黑色结点就比父结点所在子树的多,不满足红黑树的性质 5。

场景 4.2.1:插入结点是左子树

处理:将父结点变为黑色

将祖父结点变为红色

将祖父结点右旋

场景 4.2.2:插入结点是左子树

这种场景显然可以转换为 4.2.1。

处理:将父结点进行左旋

将父结点设为插入结点,得到场景 4.2.1

进行场景 4.2.1 的处理

场景 4.3:叔父结点不存在或为黑色,插入结点的父结点是祖父结点的右子结点相当于场景 4.2 的方向反转,直接看图。

场景 4.3.1:插入结点是左子树

处理:将父结点变为黑色

将祖父结点变为红色

对祖父结点进行左旋

场景 4.3.2:插入结点是右子树

处理:将父结点进行右旋

将父结点设置为插入结点,得到场景 4.3.1

进行场景 4.3.1 的处理

下面举个例子,往一棵红黑树中插入元素,整棵树的变换如下图所示:

红黑树删除

红黑树删除操作也分为两步:

定位删除的位置

定位删除位置可以复用红黑树搜索的操作。

如果不存在目标结点,忽略本次操作;如果找到目标结点,删除后进行自平衡处理。

删除后实现自平衡

二叉搜索树删除的时候可能出现三种场景:若删除结点无子结点,直接删除即可。

若删除结点只有一个子结点,用子结点替换删除结点。

若删除结点有两个子结点,用**后继结点(大于删除结点的最小结点)**替换删除结点。

具体应用,可以借助这张图理解:

我们可以发现,另外两种二叉树的删除场景都可以通过相互转换变为场景一。

在场景二情况下:删除结点用其唯一的子结点替换,子结点替换为删除结点后,可以认为删除的是子结点,若子结点又有两个子结点,那么相当于转换为场景三,一直自顶向下转换,总是能转换为场景一。

在场景三情况下:删除结点用后继结点,如果后继结点有右子结点,那么相当于转换为场景二,否则转为场景一。

综上所述,删除的结点可以看作删除替换结点,且替换结点最后总是在树末。

下面总结一下红黑树删除可能出现的所有场景:

为了方面理解,我们先约定一下结点的叫法:R:替换结点

P:替换结点的父结点

S:替换结点的兄弟结点

SL:兄弟结点的左子结点

SR:兄弟结点的右子结点

灰色:结点颜色可能是红色,也可能是黑色

注意:R 是即将被替换到删除结点的位置的替换结点,在删除前,它还在原来所在位置参与树的子平衡,平衡后再替换到删除结点的位置,才算删除完成。

①场景 1:替换结点为红色

我们把替换结点换到了删除结点的位置时,由于替换结点为红色,删除也了不会影响红黑树的平衡,只要把替换结点的颜色变为删除的结点的颜色即可重新平衡。

处理:替换结点颜色变为删除结点的颜色。

②场景 2:替换结点为黑色

当替换结点是黑色时,就必须进行自平衡处理了,我们可以通过区分替换结点是其父结点的左子结点还是右子结点,来做不同的旋转,使树重新平衡。

场景 2.1:替换结点是左子树

场景 2.1.1:替换结点的兄弟结点为红色

若兄弟结点是红结点,那么根据红黑树性质 4,兄弟结点的父结点和子结点肯定为黑色,按照下图方式处理,得到删除场景 2.1.2.3。

处理:将兄弟结点变为黑色

将父结点变为红色

对父结点进行左旋,得到场景 2.1.2.3

进行场景 2.1.2.3 的处理

场景 2.1.2:替换结点的兄弟结点为黑色

当兄弟结点为黑时,其父结点和子结点的具体颜色也无法确定,此时又得考虑多种子场景。

场景 2.1.2.1:替换结点的兄弟结点的右子结点为红色,左子结点任意颜色

即将删除的左子树的一个黑色结点,显然左子树的黑色结点少 1 了,然而右子结点又是红色,那么我们直接向右子树“借”个红结点来补充黑结点,并进行旋转处理。

如图所示:

处理:将兄弟结点的颜色变为父结点的颜色

将父结点变为黑色

将兄弟结点的右子结点变为黑色

对父结点进行左旋

场景 2.1.2.2:替换结点的兄弟结点的右子结点为黑色,左子结点为红色

兄弟结点所在的子树有红结点,又可以向兄弟子树“借”个红结点过来,这就转换回了场景 2.1.2.1。

如图所示:

处理:将兄弟结点变为红色

将兄弟结点的左子结点变为黑色

对兄弟结点进行右旋,得到场景 2.1.2.1

进行场景 2.1.2.1 的处理

场景 2.1.2.3:替换结点的兄弟结点的子结点都为黑色

处理:如果父结点为黑色:将兄弟结点变为红色;将父结点作为新的替换结点;重新进行删除结点的场景处理。

如果父结点为红色:替换结点的父结点和替换结点的兄弟结点颜色交换;删除结点和替换结点的值交换后,删除替换结点。

场景 2.2:替换结点是右子树

实际上是场景 2.1 的镜像操作。

场景 2.2.1:替换结点的兄弟结点为红色

处理:将兄弟结点变为黑色

将父结点变为红色

对父结点进行右旋,得到场景 2.2.2.3

进行场景 2.2.2.3 的处理

场景 2.2.2:替换结点的兄弟结点为黑色

处理:将兄弟结点的颜色变为父结点的颜色

将父结点变为黑色

将兄弟结点的左子结点变为黑色

对父结点进行右旋

场景 2.2.2.2:替换结点的兄弟结点的左子结点为黑色,右子结点为红色

处理:将兄弟结点变为红色

将兄弟结点的右子结点设为黑色

对兄弟结点进行左旋,得到场景 2.2.2.1

进行场景 2.2.2.1 的处理

处理:

为什么红黑树查询快_面试被问“红黑树”,我一脸懵逼…相关推荐

  1. 为什么红黑树查询快_目前最详细的红黑树原理分析(大量图片+过程推导!!!)...

    一.为什么要有红黑树这种数据结构? 我们知道ALV树是一种严格按照定义来实现的平衡二叉查找树,所以它查找的效率非常稳定,为O(log n),由于其严格按照左右子树高度差不大于1的规则,插入和删除操作中 ...

  2. 为什么红黑树查询快_红黑树为什么比二叉查找树更高效

    平衡二叉查找树 什么是平衡二叉查找树 二叉树中任意一个节点的左右子树的高度相差不能大于 1,这是一种较为严格的定义,但是实际工程中使用,不会要求这么严格,只要实际的高度不比 log2(n)大很多,能达 ...

  3. 为什么红黑树查询快_为什么工程中都喜欢用红黑树,而不是其他平衡二叉查找树呢?...

    前言 二叉查找树是最常用的一种二叉树,它支持快速插入.删除.查找操作,各个操作的时间复杂度跟树的高度成正比,理想情况下,时间复杂度是O(logn). 不过,二叉查找树在频繁的动态更新过程中,可能会出现 ...

  4. 为什么红黑树查询快_为什么红黑树的效率比较高

    红黑树属于平衡二叉树.它不严格是因为它不是严格控制左.右子树高度或节点数之差小于等于1,但红黑树高度依然是平均log(n),且最坏情况高度不会超过2log(n). 红黑树(red-black tree ...

  5. 红黑树中nil结点_什么是红黑树?程序员面试必问!

    点击上方java小组,选择"置顶公众号" 优质文章,第一时间送达 当在10亿数据中只需要进行10几次比较就能查找到目标时,不禁感叹编程之魅力!人类之伟大呀! -- 学红黑树有感. ...

  6. 图解红黑树原理,再也不怕面试被问到,不详细算我输!

    前言 最近针对互联网公司面试问到的知识点,总结出了Java程序员面试涉及到的绝大部分面试题及答案分享给大家,希望能帮助到你面试前的复习且找到一个好的工作,也节省你在网上搜索资料的时间来学习. 内容涵盖 ...

  7. java执行sql文件_面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他

    初识 MyBatis MyBatis 是第一个支持自定义 SQL.存储过程和高级映射的类持久框架.MyBatis 消除了大部分 JDBC 的样板代码.手动设置参数以及检索结果.MyBatis 能够支持 ...

  8. .jar中没有主清单属性_面试官问:为什么SpringBoot的 jar 可以直接运行?

    点击上方蓝色字体,选择"设为星标" 优质文章,及时送达 来源 | https://urlify.cn/uQvIna SpringBoot提供了一个插件spring-boot-mav ...

  9. sql参数化还是被注入了_面试官问你 SQL 注入攻击了吗?

    目录 为什么要聊 SQL 注入攻击? 什么是 SQL 注入攻击? 如何进行 SQL 注入攻击? 如何防范? 常见面试题 瞎比比 为什么要聊 SQL 注入攻击? 我这人有个想法,就是不管自己跳不跳槽,每 ...

  10. 16 bit float 存储_面试官问我存储金额应该用哪种数据类型,我竟这样回答

    前言 ​ 最近在面试时,碰到这样一个问题:在问到项目部分时,面试官问我:你的项目中用到的分数.金额之类的数字是用的什么数据类型? 我没有过多思考脱口而出:double!随后面试官又问:为啥不用floa ...

最新文章

  1. Python之向日志输出中添加上下文信息
  2. R单变量可视化(Histograms、 Index Plots、Time-Series Plots、Pie Charts)
  3. 让AngularJS的$http 服务像jQuery.ajax()一样工作
  4. window服务器cpu过高的排查_高频面试题:Java程序占用 CPU 过高怎么排查
  5. OpenCV 基本绘制Basic Drawing
  6. 同时读取两个USB摄像头采集
  7. 【C++】vs无法更新DoDataExchange方法问题解决
  8. python的指针跟c的区别_ctypes中的LP_x*指针和*p指针有什么区别?(以及与结构的奇怪交互)...
  9. linux清除cpu,解决kswapd0 CPU占用率高的问题-清除病毒
  10. 【elasticsearch】ES生命周期管理
  11. Adobe Edge Animate 1.0 概述
  12. 史上最简单的Mybatis教程(5天就够了)
  13. OpenGL ES 送显 YUV NV12
  14. Maven中pom文件常见的标签使用以及介绍
  15. linux根目录下各子目录的作用
  16. 在Android APP内部实现一个Http Server——NanoHttpd 简单剖析
  17. 安卓,应用程序无响应(ANR)
  18. MyEclipse中如何修改项目的编码格式
  19. 传真通讯技术的革新:IP传真
  20. TCP协议拥塞控制算法(Reno、HSTCP、BIC、Vegas、Westwood)

热门文章

  1. cell和battery的区别
  2. PTA---约分最简分式 (10 分)
  3. Nature:进化新方式?线粒体DNA会插入我们的基因组
  4. 【mind+ Maixduino用户库】NES 游戏扩展库
  5. 两张表之间进行数据库查询时的聚合函数用法
  6. java 注解 entity_详解Java中的注解
  7. 【跨域问题】springBoot + VUE解决跨域问题几种处理方案
  8. poi生成pptx文件以及背景颜色或者背景图片的设置
  9. STM32与DS1302设计时钟芯片,超详细
  10. 2016年总结与2017展望