数据库中的数据一般是放在磁盘里面,存取数据的时候就要访问磁盘,
物理访问过程:盘片旋转,磁臂移动 两个过程。盘片旋转到指定位置之后,移动磁臂开始进行数据的存取。
那么存取数据的时间(快慢)主要是在哪部分消耗呢?主要就是定位过程消耗的。
所以:考虑到提高存取数据的速率,实际上就是减少磁盘定位(I/O操作)的次数。
来举个例子。来顺序查找。
![](http://upload-images.jianshu.io/upload_images/13963670-768f0ac3bc9811aa.png?imageMogr2/auto-orient/strip|imageView2/2/w/513/format/webp)
查找5的时候,从头到尾的遍历,一共需要定位5次。不用再赘述,显然这样的顺序查找是最低效的。
为了提高效率,来二叉树。
二叉树的规范我就不说了,
![](http://upload-images.jianshu.io/upload_images/13963670-7e82569497152b59.png?imageMogr2/auto-orient/strip|imageView2/2/w/674/format/webp)
一共6个数,无论查找哪个数,最多也就定位3次。
嗯,既然二叉树这么方便,那大家都用二叉树好了。额,其实图一那种情况,也算是二叉树,那算是一种情况。如果无法保证提高效率的稳定性,那这种结构还是不好。
(在这里记录一个知识点)
先序,中序,后序遍历。说一点就好,这里的先中后,说的是根节点。
为了提升稳定性,来平衡二叉树。
平衡二叉树用平衡因子差值来判断是否平衡,并旋转二叉树。平衡因子:左右子树高度差。平衡二叉树里平衡因子不能超过1,否则旋转。
长叹一口气,这样稳定了吧?
稳定是稳定,但是为了二叉树的稳定,牺牲了一些更重要的东西——时间。
当新增删除数据导致的旋转二叉树时,很耗时间的!
所有的操作的目的都是为了节省时间,提高效率。这样操作舍本逐末了。但也不是丝毫没有可取之处。
使用场景:新增删除数据少,查找数据多的应用场景可以。
在稳定性的基础上,再优化一下,减少一下旋转次数吧,来红黑树。
红黑树(Java中TreeMap)特性:
1、每个节点要么是红色,要么是黑色。
2、根节点必须是黑色。
3、红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色)。
4、对于每个节点,从该点至null(树尾端)的任何路径,都含有相同个数的黑色节点。
![](http://upload-images.jianshu.io/upload_images/13963670-87fd01f2e5e545c3.png?imageMogr2/auto-orient/strip|imageView2/2/w/851/format/webp)
红黑树旋转的关键逻辑是:确保任何一个节点的左右子树的高度差不会超过二者中较低那个的一倍。举例:节点A,左子树高度X,右子树高度Y, X-Y<=min(X,Y);
所以当 X-Y > min(X,Y),然后开始旋转。
红黑树已经稳成这样了,满足吗?不满足,还可以更稳。来B树。
学习B树的时候我曾看到这样的定义。
定义1
1.根结点至少有两个子女。
2.每个中间节点都包含k-1个元素和k个孩子,其中 ceil(m/2) ≤ k ≤ m 3.每一个叶子节点都包含k-1个元素,其中 ceil(m/2) ≤ k ≤ m 4.所有的叶子结点都位于同一层。 5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域划分 6.每个结点的结构为:(n,A0,K1,A1,K2,A2,… ,Kn,An) 其中,Ki(1≤i≤n)为关键字,且Ki<Ki+1(1≤i≤n-1)。 Ai(0≤i≤n)为指向子树根结点的指针。且Ai所指子树所有结点中的关键字均小于Ki+1。 n为结点中关键字的个数,满足ceil(m/2)-1≤n≤m-1。
定义2
定义任意非叶子结点最多只有M个儿子,且M>2;
根结点的儿子数为[2, M]; 除根结点以外的非叶子结点的儿子数为[M/2, M],向上取整; 非叶子结点的关键字个数=儿子数-1; 所有叶子结点位于同一层; k个关键字把节点拆成k+1段,分别指向k+1个儿子,同时满足查找树的大小关系。
前提:M阶B树
三种结点:根结点,中间结点,叶子结点
孩子:指一个结点下的子结点
关键字:结点中的值
1、根结点孩子数量:[2,M]
2、中间结点孩子数量:[ceil(M/2), M]
3、根结点和中间结点的关键字数量:他们的孩子数量 -1
4、一个结点当中:指向孩子指针和关键字的位置关系是:(指针1) 关键字A (指针2) 关键字B (指针3) 每个关键字的值 > 左指针 指向的孩子树里结点中的关键字 每个关键字的值 < 右指针 指向的孩子树里结点中的关键字 5、叶子结点位于同一层 6、结点中的关键字从小到大排列 7、结点中关键字不重复
![](http://upload-images.jianshu.io/upload_images/13963670-d93bfcdb0c968000.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/529/format/webp)
图片和定义都祭出之后,B树对于红黑树的优势很明显了,最明显的就是B树一个结点存放了多个关键字。 将在磁盘中的定位操作移到了结点中的关键字大小比较。
B树是很可以,但并不是对所有场景都友好。来B+树。
先说说什么场景B树不友好,范围查询:比如要找上图中E->O,两个字母之间的所有字母。那B树的逻辑就是中序查找到E,继续中序查找到O,然后拿出来字母。
那么B+树对于这种场景就很简单了。来先说一说B+树基本的东西,说完也就知道了为什么用B+树范围查找比B树简单了。
1、根节点和中间结点 的 孩子数量 = 关键字数量
2、根节点和中间结点 的 关键字 是 关键字对应子树中所有关键字的最大值,同时也存在于子树中
3、根节点和中间结点 只做索引,不包含数据指针以及数据
4、叶子结点包含所有数据,并按照从小到大顺序排列
5、叶子结点用指针连在一起
![](http://upload-images.jianshu.io/upload_images/13963670-50acb9a7e47a8e7f.png?imageMogr2/auto-orient/strip|imageView2/2/w/868/format/webp)
所以,对于范围查询:比如查找3-8之间的数字,B+树的做法是直接在叶子结点上的有序链表上遍历即可。
而且,B+树比B树的查找更加稳定,因为每次找到都会到叶子结点。
那为什么B+树每次都会到叶子结点?B树每次到哪里?
B树上的关键字代表一个索引key,和它同时存在的是key所指向的内容在内存中实际的存储位置。如果遍历到了某个关键字,那么就根据指针去真实数据的存储位置。
B+树上的 非叶子节点 关键字只代表索引key,叶子结点上存储真实数据 或者 指向真实数据位置 的指针。