二叉排序树、平衡二叉树、B树&B+树、红黑树的设计动机、缺陷与应用场景
之前面试时曾被问到“如果实现操作系统的线程调度应该采用什么数据结构?”,因为我看过ucore的源码,知道ucore是采用斜堆的方式实现的,可以做到O(n)的插入、O(1)的查找。我回答了斜堆,但面试官坚持让我在B树和红黑树之间选择一个,由于实际上很少用到B树和红黑树,所以我也不太清楚,只是隐约记得红黑树用于磁盘读取比较好,好像和数据大小和数据连续性相关,显然我记错了。当时觉得红黑树还有一些应用,应该命中的可能性比较大,就随意答了红黑树。当然回答错啦,面试官还给我简单讲解了一下,一直想着有机会好好总结一下这几个树的设计动机、缺陷和应用场景。最近有些时间,决定好好复习一下,毕竟数据结构才是基础。
树
为什么要引入树呢?一般在学习数据结构时,往往先学习向量和列表,然后是栈与队列。事实上,栈与队列只是在组织数据时额外加入了一些限制,分别是先进后出和先进先出,向量和列表都可以实现这种策略。那么向量和列表又存在什么缺陷呢?一般来讲,我们需要对数据结构做静态操作和动态操作,静态操作就是查找,动态操作就是插入和删除。在C/C++中一般采用数组实现向量,采用链表实现列表。接下来我们看一下它们的静态和动态操作的时间开销。
| 向量 | 列表
- | :-: | -:
静态操作(查找) | O(1)| O(n)
动态操作(插入、删除) | O(n) | O(1)
可以看到,已有的数据结构不能很好的平衡静态操作和动态操作的时间开销。
二叉排序树、平衡二叉树
一种简单的方法是采用二叉排序树(也叫二叉搜索树,BST,Binary Search Tree),构造一颗二叉排序树十分简单。一般来讲,大于根节点的放在根节点的右子树上,小于根节点的放在根节点的左子树上(如果等于根节点,则可视情况而定),如果写程序的话,可以采用递归的方式,而且由于不存在重叠子问题的情况,因此递归的性能已经足够好(不考虑栈溢出的情况)。
二叉排序树在通常情况下可以达到O(lgN)的静态、动态操作的时间复杂度,但是存在一种特殊情况,即输入的数据本身就是有序的,这时二叉排序树退化成向量。
为了消除二叉排序树对于输入敏感的特性,引入平衡二叉树,事实上平衡二叉树应该叫平衡二叉排序树也许更合理。它采用递归的方式定义,这里我就不去查书上的标准定义了,只要保证每个节点左子树和右子树的高度差小于等于1就可以了。
平衡二叉树(也叫AVL树)就比较好用了,可以弥补二叉排序树对输入敏感的缺陷,可以确保静态和动态操作的时间复杂度为O(loN)。
B树&B+树
1981年,Bill Gates曾说过这样一段话“640KB is ought to be enough for anybody.”,可能当时intel生产的内存只有640KB,今天看来这句话也许很荒唐,目前好一点的机器都已经达到16GB的内存了,但是事实上这句话仍然有一定的道理。
只要学习过操作系统的人都知道,存储器的访问速度和容量往往成反比(这句话不是很妥当,只要领会意思就好),最快的当然是CPU上的寄存器,然后是cache(多级cache,不同硬件平台不同),然后是内存、外部硬盘等等。当两个处在不同层级上的存储器彼此之间交互数据时(例如内存与硬盘之间),我们称之为I/O,事实上这种I/O操作相当耗时,应该尽可能避免。
B树的出现就是为了解决这个问题,B树由于是多路二叉树,事实上它的高度要远低于二叉平衡树。一般来讲,我们可以认为二叉平衡树每下降一层需要执行一次磁盘I/O操作,以1G数据为例,平均需要30次磁盘I/O才能读取到数据,而B树每下降一层,每个超级结点都会读入多个关键码,需要的磁盘I/O次数小于AVL树,因此B树适用于实现磁盘的读写逻辑。
红黑树
对于向量、列表、树和图来说,它们每次的动态操作都会完全遗忘之前的状态,转而到达全新的状态,这种数据结构称为ephemeral structure。另一种数据结构可以记录某一历史时刻的状态,在访问时可以根据版本好+目标数据进行访问,这种数据结构称为persistent structure。事实上,红黑树可以实现这种对历史版本的记录,我也不懂它为什么有这么奇妙的功能,这里就不再深入研究了,因为我觉得研究明白以后我还是会忘,有机会再看。
结论
回到最初的问题,即面试官提到的问题。我觉得面试官强迫我在B树和红黑树之间选择没有多少道理,因此线程调度的数据量很小,不涉及磁盘I/O的问题,同时线程调度好像也没有记录历史版本的需求,而这两个数据结构的静态和动态操作的时间复杂度一样,空间复杂度也接近,因此它们两个应该差不多才对。话说,它们两个的实现效率都不如斜堆。
二叉排序树、平衡二叉树、B树&B+树、红黑树的设计动机、缺陷与应用场景的更多相关文章
- 1.红黑树和自平衡二叉(查找)树区别 2.红黑树与B树的区别
1.红黑树和自平衡二叉(查找)树区别 1.红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单. 2.平衡 ...
- 数据结构和算法(Golang实现)(29)查找算法-2-3树和左倾红黑树
某些教程不区分普通红黑树和左倾红黑树的区别,直接将左倾红黑树拿来教学,并且称其为红黑树,因为左倾红黑树与普通的红黑树相比,实现起来较为简单,容易教学.在这里,我们区分开左倾红黑树和普通红黑树. 红黑树 ...
- 从二叉搜索树到AVL树再到红黑树 B树
这几种树都属于数据结构中较为复杂的,在平时面试中,经常会问理解用法,但一般不会问具体的实现,所以今天来梳理一下这几种树之间的区别与联系,感谢知乎用户@Cailiang,这篇文章参考了他的专栏. 二叉查 ...
- 从二叉查找树到平衡树:avl, 2-3树,左倾红黑树(含实现代码),传统红黑树
参考:自平衡二叉查找树 ,红黑树, 算法:理解红黑树 (英文pdf:红黑树) 目录 自平衡二叉树介绍 avl树 2-3树 LLRBT(Left-leaning red-black tree左倾红黑树 ...
- 简述树,Trie,Avl,红黑树
树的表示方法 在平时工作中通常有2种方式来表示树状结构,分别是孩子链表示法和父节点表示法.光说名词可能无法让人联系到实际场景中,但是写出代码之后大家一定就明白了. 孩子链表示法,即将树中的每个结点的孩 ...
- 红黑树、B(+)树、跳表、AVL等数据结构,应用场景及分析,以及一些英文缩写
在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间 ...
- 大名鼎鼎的红黑树,你get了么?2-3树 绝对平衡 右旋转 左旋转 颜色反转
前言 11.1新的一月加油!这个购物狂欢的季节,一看,已囊中羞涩!赶紧来恶补一下红黑树和2-3树吧!红黑树真的算是大名鼎鼎了吧?即使你不了解它,但一定听过吧?下面跟随我来揭开神秘的面纱吧! 一.2-3 ...
- 红黑树与AVL树
概述:本文从排序二叉树作为引子,讲解了红黑树,最后把红黑树和AVL树做了一个比较全面的对比. 1 排序二叉树 排序二叉树是一种特殊结构的二叉树,可以非常方便地对树中所有节点进行排序和检索. 排序二叉树 ...
- AVL树、splay树(伸展树)和红黑树比较
AVL树.splay树(伸展树)和红黑树比较 一.AVL树: 优点:查找.插入和删除,最坏复杂度均为O(logN).实现操作简单 如过是随机插入或者删除,其理论上可以得到O(logN)的复杂度,但是实 ...
随机推荐
- 2017-07-01(ifconfig ifdown ifup netstat )
ifconfig 可以查看IP地址与子网掩码 ifdown(禁用网卡) ifdown 网络设备名 ifdown eth0 ifup (启动网卡) ifup 网络设备名 ifup eth0 net ...
- 利用mk-table-checksum监测Mysql主从数据一致性操作记录
前面已经提到了mysql主从环境下数据一致性检查:mysql主从同步(3)-percona-toolkit工具(数据一致性监测.延迟监控)使用梳理今天这里再介绍另一种Mysql数据一致性自动检测工具: ...
- TemplateMethod-模板模式
什么是Template Method模式 在父类中定义处理流程的框架,在子类中实现具体处理的模式就称为Template Mehtod模式.模板模式的关键是:子类可以置换掉父类的可变部分,但是子类却不可 ...
- rpm包
rpm包有什么命名规则与依赖? 命令规则: 包名-版本号.发布次数-linux平台.l.硬件平台.rpm 依赖: 树型依赖:a --> b --> c 安装a包需要安装b包,安装b包需要安 ...
- margin 和 padding 的本质区别
问题? 如何弄清 margin 和 padding之间的区别? 那,答案呢? margin 边界, padding 填充 假如有一个盒子,padding就相当于盒子的厚度,盒子大小固定,通过修改pad ...
- 爬取知名社区技术文章_items_2
item中定义获取的字段和原始数据进行处理并合法化数据 #!/usr/bin/python3 # -*- coding: utf-8 -*- import scrapy import hashlib ...
- getResource()和getSystemResource()分析
1. getClass().getResource() 第一步,getClass().getResource(path)是有一个路径参数的,这个路径会先被转换成"类所在的包名称+path&q ...
- C# 类型基础(上)
C#类型都派生自System.Object 祖先的优良传统:Object的公共方法 Equals: 对象的同一性而非相等性 GetHashCode:返回对象的值的哈希码 ToString:默认返回类型 ...
- Android图像数据传递到C++的一些坑
最近在做一个Android图象识别的app, 通过相机预览或者是拍照功能获取图像数据,然后将图像数据传递到本地C++的图像识别so库.在这个过程中花的时间最多的就是数据传输问题.谨以此坑,警示未来! ...
- Python元编程
简单定义"元编程是一种编写计算机程序的技术,这些程序可以将自己看做数据,因此你可以在运行时对它进行内审.生成和/或修改",本博参考<<Python高级编程>> ...