前面介绍了二叉查找树(Binary Search Tree),他对于大多数情况下的查找和插入在效率上来说是没有问题的,但是他在最差的情况下效率比较低。本文及后面文章介绍的平衡查找树的数据结构能够保证在最差的情况下也能达到lgN的效率,要实现这一目标我们需要保证树在插入完成之后始终保持平衡状态,这就是平衡查找树(Balanced Search Tree)。在一棵具有N 个节点的树中,我们希望该树的高度能够维持在lgN左右,这样我们就能保证只需要lgN次比较操作就可以查找到想要的值。不幸的是,每次插入元素之后维持树的平衡状态太昂贵。所以这里会介绍一些新的数据结构来保证在最坏的情况下插入和查找效率都能保证在对数的时间复杂度内完成。本文首先介绍2-3查找树(2-3 Search Tree),后面会在此基础上介绍红黑树和B树。

定义

和二叉树不一样,2-3树运行每个节点保存1个或者两个的值。对于普通的2节点(2-node),他保存1个key和左右两个自己点。对应3节点(3-node),保存两个Key,2-3查找树的定义如下:

1. 要么为空,要么:

2. 对于2节点,该节点保存一个key及对应value,以及两个指向左右节点的节点,左节点也是一个2-3节点,所有的值都比key有效,有节点也是一个2-3节点,所有的值比key要大。

3. 对于3节点,该节点保存两个key及对应value,以及三个指向左中右的节点。左节点也是一个2-3节点,所有的值均比两个key中的最小的key还要小;中间节点也是一个2-3节点,中间节点的key值在两个跟节点key值之间;右节点也是一个2-3节点,节点的所有key值比两个key中的最大的key还要大。

如果中序遍历2-3查找树,就可以得到排好序的序列。在一个完全平衡的2-3查找树中,根节点到每一个为空节点的距离都相同。

查找

在进行2-3树的平衡之前,我们先假设已经处于平衡状态,我们先看基本的查找操作。

2-3树的查找和二叉查找树类似,要确定一个树是否属于2-3树,我们首先和其跟节点进行比较,如果相等,则查找成功;否则根据比较的条件,在其左中右子树中递归查找,如果找到的节点为空,则未找到,否则返回。查找过程如下图:

插入

往一个2-node节点插入

往2-3树中插入元素和往二叉查找树中插入元素一样,首先要进行查找,然后将节点挂到未找到的节点上。2-3树之所以能够保证在最差的情况下的效率的原因在于其插入之后仍然能够保持平衡状态。如果查找后未找到的节点是一个2-node节点,那么很容易,我们只需要将新的元素放到这个2-node节点里面使其变成一个3-node节点即可。但是如果查找的节点结束于一个3-node节点,那么可能有点麻烦。

往一个3-node节点插入

往一个3-node节点插入一个新的节点可能会遇到很多种不同的情况,下面首先从一个最简单的只包含一个3-node节点的树开始讨论。

只包含一个3-node节点

如上图,假设2-3树只包含一个3-node节点,这个节点有两个key,没有空间来插入第三个key了,最自然的方式是我们假设这个节点能存放三个元素,暂时使其变成一个4-node节点,同时他包含四个子节点。然后,我们将这个4-node节点的中间元素提升,左边的节点作为其左节点,右边的元素作为其右节点。插入完成,变为平衡2-3查找树,树的高度从0变为1。

节点是3-node,父节点是2-node

和第一种情况一样,我们也可以将新的元素插入到3-node节点中,使其成为一个临时的4-node节点,然后,将该节点中的中间元素提升到父节点即2-node节点中,使其父节点成为一个3-node节点,然后将左右节点分别挂在这个3-node节点的恰当位置。操作如下图:

节点是3-node,父节点也是3-node

当我们插入的节点是3-node的时候,我们将该节点拆分,中间元素提升至父节点,但是此时父节点是一个3-node节点,插入之后,父节点变成了4-node节点,然后继续将中间元素提升至其父节点,直至遇到一个父节点是2-node节点,然后将其变为3-node,不需要继续进行拆分。

根节点分裂

当根节点到字节点都是3-node节点的时候,这是如果我们要在字节点插入新的元素的时候,会一直查分到跟节点,在最后一步的时候,跟节点变成了一个4-node节点,这个时候,就需要将跟节点查分为两个2-node节点,树的高度加1,这个操作过程如下:

本地转换

将一个4-node拆分为2-3node涉及到6种可能的操作。这4-node可能在跟节点,也可能是2-node的左子节点或者右子节点。或者是一个3-node的左,中,右子节点。所有的这些改变都是本地的,不需要检查或者修改其他部分的节点。所以只需要常数次操作即可完成2-3树的平衡。

性质

这些本地操作保持了2-3树的平衡。对于4-node节点变形为2-3节点,变形前后树的高度没有发生变化。只有当跟节点是4-node节点,变形后树的高度才加一。如下图所示:

分析

完全平衡的2-3查找树如下图,每个根节点到叶子节点的距离是相同的:

2-3树的查找效率与树的高度是息息相关的。

  • 在最坏的情况下,也就是所有的节点都是2-node节点,查找效率为lgN
  • 在最好的情况下,所有的节点都是3-node节点,查找效率为log3N约等于0.631lgN

距离来说,对于1百万个节点的2-3树,树的高度为12-20之间,对于10亿个节点的2-3树,树的高度为18-30之间。

对于插入来说,只需要常数次操作即可完成,因为他只需要修改与该节点关联的节点即可,不需要检查其他节点,所以效率和查找类似。下面是2-3查找树的效率:

实现

直接实现2-3树比较复杂,因为:

  1. 需要处理不同的节点类型,非常繁琐
  2. 需要多次比较操作来将节点下移
  3. 需要上移来拆分4-node节点
  4. 拆分4-node节点的情况有很多种

2-3查找树实现起来比较复杂,在某些情况插入后的平衡操作可能会使得效率降低。在2-3查找树基础上改进的红黑树不仅具有较高的效率,并且实现起来较2-3查找树简单。

但是2-3查找树作为一种比较重要的概念和思路对于后文要讲到的红黑树和B树非常重要。希望本文对您了解2-3查找树有所帮助。

[转]浅谈算法和数据结构: 八 平衡查找树之2-3树相关推荐

  1. 浅谈算法和数据结构: 五 优先级队列与堆排序

    原文:浅谈算法和数据结构: 五 优先级队列与堆排序 在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象.最简单的一个例子就是,在手机上玩游戏 ...

  2. 浅谈算法和数据结构: 七 二叉查找树

    前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的时候具有较高的灵活性,而有序数组在查找时具有较高的效率,本文介绍的二叉查找树(Binary Search Tree,BST)这一数据结构 ...

  3. 浅谈算法和数据结构: 哈希表

    作者: yangecnu(yangecnu's Blog on 博客园) 出处:http://www.cnblogs.com/yangecnu/  http://www.cnblogs.com/yan ...

  4. php链表和联表的区别,PHP_浅谈PHP链表数据结构(单链表),链表:是一个有序的列表,但 - phpStudy...

    浅谈PHP链表数据结构(单链表) 链表:是一个有序的列表,但是它在内存中是分散存储的,使用链表可以解决类似约瑟夫问题,排序问题,搜索问题,广义表 单向链表,双向链表,环形链表 PHP的底层是C,当一个 ...

  5. php mysql 链表_浅谈PHP链表数据结构(单链表)

    链表:是一个有序的列表,但是它在内存中是分散存储的,使用链表可以解决类似约瑟夫问题,排序问题,搜索问题,广义表 单向链表,双向链表,环形链表 PHP的底层是C,当一个程序运行时,内存分成五个区(堆区, ...

  6. 浅谈python高级数据结构—— 字符串(str)

    浅谈python高级数据结构-- 字符串(str) 在python中字符串可以说的运用的特别多了.在当我们input 输入的时候,也是一个str字符串类型,我们今天就来简单的说一下(字符串)类型的一些 ...

  7. 【数据结构】折半查找及其二叉判定树画法

    折半查找又叫二分查找,是数据结构中一种很重要的查找方式. 其特点有以下几个: 只能对有序的顺序表进行查找. 是一种静态查找. 查找的平均时间复杂度为o(log2n). 成功平均查找长度ASL约log2 ...

  8. 算法工程师_浅谈算法工程师的职业定位与发展

    随着大数据和以深度学习为代表的人工智能技术的飞速发展,算法工程师这个职业逐渐成为国内互联网行业的标配.2016年3月,谷歌旗下DeepMind公司的围棋程序"AlphaGo"战胜职 ...

  9. 浅谈Java的数据结构

    先上完整图 数据结构:计算机中存储.组织数据的方式. 分类:主要是三类,所有的容器数据结构都是这3个基本接口的扩展实现. 其中(Legacy)标注的代表线程安全, 可以用顺口溜:喂(Vector-对标 ...

  10. 从石头剪刀布浅谈算法的作用

    刚开始学习C语言的时候,常常听到前辈说,C语言的核心就是算法.但是对于小白来说,常常一脸懵逼,搞不懂啥叫算法?算法有什么用?我的if-else语句照样可以走天下.但是作为小白来说虽然不懂但是也不敢问, ...

最新文章

  1. aos make 配置环境
  2. 基于 APIGateway 打造生产级别的 Knative 服务
  3. 设计模式理解:装饰模式Decorator
  4. linux mysql date 格式_关于MySQL中的三种日期类型
  5. QT乱码总结4.细谈本地编码
  6. SAP Spartacus Org unit detail实例的单例特性
  7. java xsd 解析 xml文件_Java针对XSD文件验证XML文件的最佳方法是什么?
  8. 车林通购车之家--购车计算器模块--保险
  9. webpack 编译完成执行代码
  10. window删除文件夹时提示源路径太长无法删除的解决办法
  11. matlab配置vlfeat工具箱
  12. 用c语言输入年份 月份 显示天数,java输入月份,年份,显示对应月份的天数,...
  13. quora 分享链接
  14. 性能监控 TP 指标
  15. 视频监控远程传输方案
  16. 【Writeup】2017陕西网络空间安全技术大赛CSTC misc部分
  17. vs2017开发ActiveX(主讲OCX)(七)、方法
  18. 利用这5个办法成为自由职业者,通过远程赚钱,开心旅行、轻松赚钱!
  19. [转]“子不语怪力乱神”新解
  20. 系统时间被修改--病毒的处理

热门文章

  1. Pycharm中的Django项目连接mysql数据库(包含出现的错误)
  2. python 物联网服务器_python+树莓派实现IoT(物联网)数据上传到服务器
  3. 阻止滑动事件_重温DOM事件流,捕获、冒泡、useCapture、passive
  4. 7-8 猜数字 (20 分)
  5. Gym - 102163M
  6. 用homebrew 升级安装python3.7 之后系统的python版本还是旧的怎么办
  7. Leading and Trailing LightOJ - 1282
  8. adb命令查看报名和查看手机分辨率
  9. WCF---服务发布的步骤
  10. 简单易用的安装文件制作工具NSIS的使用demo示例