红黑树一:从二叉树、2-3树到红黑树,一步步讲解红黑树的来源

目录

  • 1 引言
  • 2 从二叉查找树到红黑树的演变
    • 2.1 二叉查找树
    • 2.2 平衡二叉查找树
    • 2.3 2-3树
    • 2.4 红黑树

1 引言

RB-Tree,即红黑树,它的定义如下:

  1. 这是一颗二叉树,且每个节点要么是红色、要么是黑色
  2. 根节点是黑色
  3. 叶子节点也是黑色的,且叶子节点不存储数据,即叶子节点是nil空节点
  4. 不能出现连续的红色节点,即相邻的红色节点必须被黑色节点隔开
  5. 任何一个节点到达其任意一个叶子节点均包含相同数目的黑色节点

单看上面的定义,大家肯定跟我一样一头雾水,别急,下面我们从最简单的二叉查找树说起,慢慢撕开这个高级数据结构的神秘面纱。

2 从二叉查找树到红黑树的演变

2.1 二叉查找树

一棵二叉查找树,每个节点的左节点均比当前节点小,且右节点均比当前节点大。上图的两棵树,均属于二叉查找树。于是,查找的过程如下:

  • 当前节点等于目标值时,查找成功
  • 当查找到空节点时,查找结束,返回不成功
  • 小于时,继续在左子树中查找
  • 大于时,继续在右子树中查找

最好的情况下,上图左侧示例,当所有节点的左右子树的高度都相同时,无论查找值是什么,每遍历一个节点都能排除一半的可能,这种情况的时间复杂度是O(logN)
最坏的情况下,上图右侧示例,当所有节点都集中在一条路径上,此时二叉查找树已经退化为一个链表,时间复杂度增大到O(N)
随着不断的增删操作,二叉查找树的时间复杂度变得不可控,介于O(logN)和O(N)之间。

有没有可能一直控制在最好的情况下呢,请看下一节:平衡二叉查找树

2.2 平衡二叉查找树

上一节讲到二叉查找树在最坏情况下会退化为链表,如果能保证每个节点的左右子树都是平衡的,即任意节点的左右子树的高度差均不大于1,那么查找的时间复杂度可以稳定在O(logN)。这就是平衡二叉查找树。
平衡二叉查找树的时间复杂度稳定在O(logN)。
但它同时给插入和删除操作带来了麻烦,每次插入或删除后,我们均需要确认这次操作是否影响了整棵树的平衡性,并在全局范围内作出适当调整。这种全局性对编码要求很高,难以实现且不说,额外的维持平衡的逻辑也大大增加了每次插入或删除的消耗,时间复杂度并不比普通二叉查找树优秀。

所以接下来大家不难想到,能否设计一种数据结构能够把全局的平衡性调整简化为局部的调整,从而简化代码实现难度,进而也降低时间复杂度?我们继续看下一节:2-3树

2.3 2-3树

2-3树是由2节点或3节点组成的树结构。这里的2或3指的是每个节点的子树数量:

  • 2节点:保存1个key,左子树均小于key,右子树均大于key
  • 3节点:保存2个key,左子树均小于最小key,中子树介于两个key之间,右子树均大于key
  • 相应的,我们可以得出4节点的定义,即保存3个key,同时划分出4个区间对应于4棵子树

一棵完整的2-3树,高度平衡,即任意节点到达其任意叶子节点的路径长度均相同,也即任意叶子节点的深度是相同的。所以一棵完整的2-3树,就是一棵满的2叉和3叉树,复杂度:

  • 最好情况:整棵树均由3节点组成,每遍历一个节点能排除2/3的数据,时间复杂度是 2/3 O(logN)
  • 最坏情况:整棵树均由2节点组成,就是一棵满的平衡二叉树,时间复杂度是O(logN)

完整的2-3树的查找时间复杂度:O(logN)

接下来我们以插入操作为例,来看看2-3树是怎么把平衡二叉树要求的全局平衡性调整简化为局部的调整:

对单个节点的插入处理(如上图)

  1. 插入S到空节点:直接生成S节点(这一步只可能发生在整棵树为空时。二叉树的插入是在叶子节点后插入一个新节点,树是自顶向下生长的;而2-3树则要求
  2. 插入H到2节点:得到一个3节点(H S)
  3. 插入K到3节点:
    3.1 得到一个临时4节点(H K S)
    3.2 临时4节点裂变成3个2节点:将中间key拿出来得到K节点,最小/最大key分别为K节点的左右子节点

注意第1步只可能发生在整棵树为空时。

  • 二叉树是自顶向下生长:插入操作是在叶子节点后新建一个子节点
  • 2-3树是自底向上生长:数据插入有值的叶子节点中,随着插入不断进行,底层节点不断往上裂变,直到根节点由4节点裂变成3个2节点时,树高才会+1

插入操作在一棵树内的处理(如上图)

4.将D节点插入一棵现有的树时,会触发自底向上的递归式调整:
4.1 得到一个临时4节点node1(A B D)
4.2 node1裂变,由于父节点(H S)已存在,所以中间key插入到父节点,又得到一个临时节点node2(B H S)
4.3 node2裂变,由于Node2就是根节点,所以整棵树的高度+1,新的根节点是node2的中间key(H)

上面的1/2/3/4这4个步骤,即是对一棵2-3树的插入操作可能涉及的情况,不难发现,对2-3树的插入可以是一个递归过程,每次只处理单个节点即局部的逻辑,整棵树是自平衡的。递归逻辑如下:

  • 根节点为空时,在根节点中插入key
  • 根节点非空时:
  • 先执行搜索,找到相同的key则替换;
  • 未找到key时,将key插入当前节点,即查找的最后一个节点:
    A. 当前节点为2节点,则插入后得到一个3节点,结束递归
    B. 当前节点为3节点,则插入后得到一个4节点,裂变:
    a. 当前节点为根节点,则裂变后结束递归
    b. 否则,将中间key拿出插入到当前节点的父节点,以父节点作为当前节点继续第A步

2-3树的删除操作在此省略。

现在我们知道2-3树在平衡二叉树的基础上进行了优化,保持平衡即查找复杂度O(logN)的同时能够将每次的调整都控制在局部范围内。但2-3树的实现需要处理两种类型的节点,即2节点和3节点,这就增加了代码实现的复杂度,同时这个复杂的代码引起的消耗也不小,所以2-3树的插入或删除时的时间复杂度还不够理想。

所以接下来就是重头戏了,能否用一种节点来实现2-3树,即用二叉树来实现2-3树,将查找、插入、删除、修改的时间复杂度都控制在一个合理的O(logN)?

2.4 红黑树

上一节的2-3树,其中的3节点如果我们用一个红黑的链接来表示,那么一棵2-3树可以转换成这样:

上图中我们对一棵2-3树作了处理,2节点固定为黑色,而3节点可以分解为2个用红色链接连接的2节点,然后将红色链接指向的孩子节点标记为红色,其他节点默认为黑色。事实上这样处理之后,我们就得到了一个红黑树。(注意,上图我们按左倾的方式拆分3节点,同样也可以右倾,即小key作为父节点,大key作为孩子节点)

红黑树既是二叉搜索树,又是特殊的2-3树,现在我们再以2-3树的性质来分析红黑树的定义,就容易理解了:

  1. 这是一颗二叉树,且每个节点要么是红色、要么是黑色
  2. 根节点是黑色
  3. 叶子节点也是黑色的,且叶子节点不存储数据,即叶子节点是nil空节点
  4. 不能出现连续的红色节点,即相邻的红色节点必须被黑色节点隔开
  5. 任何一个节点到达其任意一个叶子节点均包含相同数目的黑色节点

第1条略过。
第3条是为了后续代码实现的便利性而设定的要求
第2/4条,其实是因为根据上面的转换方式,一个3节点分解成一个黑色的父节点和一个红色的孩子节点,那么红色节点一定会存在一个黑色的父节点。于是根节点没有父节点所以只能是黑色;红色节点的父节点一定是黑色的所以不可能存在相邻的红色节点。
第5条也好理解,正是将红色节点去除,即复原3节点后,能够得到一棵完整的2-3树,树的每个节点的左右子树的高度相等。

红黑树以二叉树的结构实现了一棵特殊的2-3树,由于拆分了3节点,所以树高增加了,那么查找的时间复杂度还有保证吗?

  • 最好的情况:所有节点的左右子树的高度一致时,就是一棵满的平衡二叉树,时间复杂度是O(logN)
  • 最坏的情况:红黑树最不平衡的状态就是本节开头画的那棵,红色节点全部集中在一条链路上。这时查找只有黑色节点的分支时,需要遍历的节点数小于O(logN),时间复杂度就是O(logN)。查找红色节点所在的分支时,由于红色节点总是被黑色节点隔开,所以查找路径需要经历的红色节点数最大等于黑色节点数,而根据红黑树定义第5条,从根节点到叶子节点的任意路径的黑色节点固定,同样也小于O(logN),所以最坏的时间复杂度是小于O(logN)乘以2的。可以认为最坏的时间复杂度近似于O(logN)

所以红黑树的查找时间复杂度稳定在O(logN)。

原文地址:https://www.cnblogs.com/JoZSM/p/11784163.html

时间: 2024-10-12 03:40:21

红黑树一:从二叉树、2-3树到红黑树,一步步讲解红黑树的来源的相关文章

B树、B+树、红黑树、AVL树

定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2.除根结点以外的非叶子结点的儿子数为 M/2(向上取整) ~ M3.拥有 K 个孩子的非叶子节点包含 k-1 个keys(关键字),且递增排列4.所有叶子结点在同一层,即深度相同 (叶节点可以看成是一种外部节点,不包含任何关键字信息) 在B-树中,每个结点中关键字从小到大排列,并且当该结点的孩子是非叶

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

从二叉树到2-3-4树再到红黑树

直接进入正题: 一.如何从数组生成一个二叉树 假设数组为:{ 30, 13, 7, 43, 23, 12, 9, 33, 42, 21, 18, 6, 3, 50 },我们不对数组排序,直接生成二叉树. 创建流程: 1.将第一数作为根节点: 2.插入13,13小于30,放在30的左边子节点. 3.插入7,7小于30,7小于13,放在13的左边子节点. 4.插入43,43大于30,放在30的右边子节点. 5.放入23,23小于30,23大于13,放入13的右边子节点. 6.放入12,12小于30,

《 常见算法与数据结构》平衡查找树(2)——红黑树(附动画)

本系列文章主要介绍常用的算法和数据结构的知识,记录的是<Algorithms I/II>课程的内容,采用的是"算法(第4版)"这本红宝书作为学习教材的,语言是java.这本书的名气我不用多说吧?豆瓣评分9.4,我自己也认为是极好的学习算法的书籍. 通过这系列文章,可以加深对数据结构和基本算法的理解(个人认为比学校讲的清晰多了),并加深对java的理解. 红黑树介绍 红黑树是一种简单的实现2-3树的数据结构,它方便的把我们之前实现的二叉搜索树改造成了一棵2-3树.它的核心思想

浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的时候具有较高的灵活性,而有序数组在查找时具有较高的效率,本文介绍的二叉查找树(Binary Search Tree,BST)这一数据结构综合了以上两种数据结构的优点. 二叉查找树具有很高的灵活性,对其优化可以生成平衡二叉树,红黑树等高效的查找和插入数据结构,后文会一一介绍. 一 定义 二叉查找树(B

通过2-3树理解红黑树

一.简介 前面的文章我们循序渐进的讲解了<二叉树><二分搜索树><AVL-平衡二叉树>,从左至右互为基础.尤其是二分搜索树给了我们如何将数据组织成为搜索树的思想,当然二分搜索树存在的天然问题--在极端情况下回退化为链表.所以引出了AVL-平衡二叉树,通过再平衡即LL,LR,RR,RL四个旋转操作维护了一棵平衡的二分搜索树.本章节我们继续梳理一个高阶的树结构即:红黑树.想必大家都知道,红黑树如何维持平衡,如何进行颜色反转让人很难理解,虽然很多博文很多书对红黑树都有讲解,但

[数据结构]二叉搜索树(BST) VS 平衡二叉排序树(AVL) VS B树(平衡多路搜索树) VS B+树 VS 红黑树(平衡二叉B树)

1 二叉排序树/二叉查找树/Binary Sort Tree 1种对排序和查找都很有用的特殊二叉树 叉排序树的弊端的解决方案:平衡二叉树 二叉排序树必须满足的3条性质(或是具有如下特征的二叉树) 若它的左子树不为空,则:左子树上所有结点的值< 它根结点的值 若它的右子树不为空,则:右子树上所有结点的值 > 它根结点的值 它的左子树.右子树也分别为二叉排序树(递归性) (按照如上定义,即: 1 无键值相等的结点 2 中序遍历一颗二叉树时,可得一个结点值递增的有序序列) 2 平衡二叉排序树/Bal

高级树、AVL 树和红黑树

高级树.AVL 树和红黑树 二叉树遍历 Pre-order/In-order/Post-orde 前序(Pre-order):根-左-右 中序(In-order):左-根-右 后序(Post-order):左-右-根 示例代码 def preorder(self, root): if root: self .traverse_path.append(root.val) self .preorder(root.left) self .preorder(root.right) def inorder

AVL树,红黑树,B-B+树,Trie树原理和应用

前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作(如插入删除等等) 目录 AVL树 AVL树原理与应用 红黑树 红黑树原理与应用 B/B+树 B/B+树原理与应用 Trie树 Trie树原理与应用 AVL树 简介: AVL树是最早的自平衡二叉树,在早期应用还相对来说比较广,后期由于旋转次数过多而被红黑树等结构取代(二者都是用来搜索的),AVL树内