红黑树理论与实现(待补充)

红黑树的定义与性质

  • 红黑树是一种含有红黑结点,并能自平衡的二叉查找树
    在这里插入图片描述

具有以下性质

  • 每个结点要么是黑色,要么是红色
  • 根结点是黑色
  • 每个叶子结点(NIL)是黑色
  • 每个红色结点的两个子结点一定都是黑色
  • 任意一个结点到每个叶子结点的路径都包含相同数量的黑结点

其他相似性质定义

  • 红链接均为左链接(左子结点是红色的)
  • 两个红链接不能相连在一起(根结点是黑色)
  • 红黑树是完美黑色平衡
  • 空结点的链接颜色是黑色

在实际代码中,可以用常量来表示红黑,调用一个方法可判定是黑是红。代码示例:

private boolean isRed(Node node){
        if(node == null) return false;
        return node.color == RED;
    }

红黑树自平衡最小单元

红黑树的自平衡每次只考虑 CPGU 三代即可,其余部分无需考虑

  • 祖父 G
  • 父母 P
  • 叔叔 U
  • 兄弟 B
  • 根 R
    当前新增结点 C
    在这里插入图片描述

红黑树自平衡的原子操作

红黑树的自平衡操作包括:变色、旋转
其中旋转又可以分为左旋右旋。旋转要有圆心,方向
在这里插入图片描述
左旋转
在这里插入图片描述
右旋转
在这里插入图片描述
旋转过程的分解分析可以参考 AVL 树的旋转
AVL 树的旋转过程

重点:红黑树的增加与删除,都得遵循最基础的几个性质!!即所有的 “变色”、“旋转” 操作都是为了使得该红黑树满足其性质:

  • 根结点是黑色的
  • 红色结点的两个子结点是黑色的
  • 每个叶子结点 Nil 是黑色的
  • 任意一个结点到叶子结点路径中黑色结点的个数相同

红黑树的新增操作(图例)

时间复杂度:O(nlog(n))

需要注意的情况

新增结点需要考虑父结点的情况
在这里插入图片描述
新增结点的各种情况

  • 情况1:C = root
  • 情况2:C.parent = black
  • 情况3:C.parent = red && C.uncle = red
  • 情况4:C.parent = red && (C.uncle = black || C.uncle is Nil)

对于以上四种情况的新增
在这里插入图片描述

图例

该图例仅帮助了解 变色旋转 操作过程,不一定是最优方法。只需要懂得变色与旋转在平衡一颗红黑树是怎样操作即可。

  • 初始红黑树(叶子结点都是 NIL)在这里插入图片描述

  • 向红黑树中插入 结点 21(新插入的结点都是红色
    在这里插入图片描述
    从图可发现,该树违反了红黑树的性质:红色结点(22)的两个子结点应该都是黑色的

  • 调整红黑树——变色(截取红黑树部分,由前文提到的红黑树最小平衡单元——只考虑三代
    在这里插入图片描述
    其实可以发现,该红黑树又违反了红黑树性质之——任一结点到叶子结点路径中的黑色结点个数相同
    如 25 到 22 右子结点,跟 25 到 27 左子结点路径个数不同:3,2。

  • 继续将结点 25 的颜色变成红色
    在这里插入图片描述
    此处违反红黑树性质之——每个红色结点的两个子结点都是黑色:结点 25 、27 都是红色

  • 将结点 27 变成黑色
    在这里插入图片描述

  • 此时整个红黑树图
    在这里插入图片描述
    可以看到结点 17 、25 是连续的两个红色结点。此时不应该再通过 变色 调整红黑树(如果继续变色,即会让结点17变成黑色,根结点 13 变成红色,且13红色结点的左子结点为红色右子结点为黑色,违反两个性质),而是应该通过 旋转

  • 从根结点开始左旋转
    在这里插入图片描述

在这里插入图片描述

  • 将根结点变成黑色
    在这里插入图片描述
    此时看起来像是调整好了,实际上根结点的左子树中仍违反了性质——任一结点到叶子结点路径中黑色结点个数一样
    17->8->6->NIL:4
    17->15->NIL:3

  • 在根结点的左子树中发生 右旋转
    在这里插入图片描述
    在这里插入图片描述

  • 最后观察整个红黑树,再进行一次 变色 既可
    在这里插入图片描述
    观察发现,符合红黑树的所有性质

  • 根结点是黑色的

  • 红色结点的两个子结点是黑色的

  • 每个叶子结点 Nil 是黑色的

  • 任意一个结点到叶子结点路径中黑色结点的个数相同

该例子操作过程:
变 色 − > 左 旋 转 − > 变 色 − > 右 旋 转 − > 变 色 变色 -> 左旋转 -> 变色 -> 右旋转 -> 变色 >>>>

PS:该案例目的不是最佳调整方案,主要了解变色与旋转操作。

红黑树的代码实现

了解即可…
参考文章
参考实现代码
Java代码实现源码

红黑树与平衡二叉树的对比

  • 平衡树(AVL)更平衡,结构上更加直观,时间效能针对读取而言更高,但维护较难,即每次插入、删除之后都需要重新维护树的平衡。
  • 红黑树通过规则的设定,确保了插入、删除的最坏的时间复杂度是O(log n)
  • 红黑树解决了平衡树(AVL)维护成本高的问题放弃了追求完美平衡。红黑树的读取略逊于 AVL,维护性比平衡树强,每次插入、删除的平均旋转次数远小于平衡树(最多只需要三次旋转
  • 平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新结点之后需要旋转的次数不能预知

红黑树与 B+ 树用途的区别

  • 红黑树多用在内部排序,即全放在内存中,STL的map和set的内部实现就是红黑树
  • B+树多用在外存上,B+也被称为一个磁盘友好的数据结构

为什么 B+ 树磁盘友好??

  • 磁盘读写代价更低
  • 查询效率更加稳定
  • 遍历所有的数据更方便

为什么 MySql 索引使用 B+ 树而不使用红黑树

  • B+ 树就是为文件存储而生的。
  • 查询新能上比较
    数据库文件存储在磁盘中,定位一行信息需要查找该行文件所在柱面号,磁盘号,扇区号,页号,这个阶段十分耗时。每一次的定位请求意味着做一次 IO 操作,也意味着成倍的时间消耗。因此减少 IO 查询时提高性能查询的关键。而 IO查询的次数就是索引树的高度,高度越低查询的次数越少。同样的结点次数 红黑树的高度最多为 2log(n+1),而 B+ 树的高度最多为 (log t(n+1)/2)+1,随着 t 增大高度会更小,IO 次数也会减少

B+树的查询时间大概是 log(n)
hash 存储索引查询时间 o(1)

为什么不用hash?

  • 这与业务场景有关。如果只选一个数据,hash更快,但数据库中经常会选择多条,这时候由于B+树索引有序,并且又有链表相连,它的查询效率比hash就快很多了。
  • 而且数据库中的索引一般是在磁盘上,数据量大的情况可能无法一次装入内存,B+树的设计可以允许数据分批加载,同时树的高度较低,提高查找效率。

小结

  • 红黑树是平衡二叉树的一种,他保证在最坏情况下基本动态集合操作的时间复杂度为 O(log n)

  • 具有以下性质

    • 每个结点要么是黑色,要么是红色
    • 根结点是黑色
    • 每个叶子结点(NIL)是黑色
    • 每个红色结点的两个子结点一定都是黑色
    • 任意一个结点到每个叶子结点的路径都包含相同数量的黑结点
  • 可通过 变色旋转 调整红黑树完成插入、删除操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值