数据结构
学习之前要先了解二叉搜索树,平衡二叉树(AVL树),B-树,B+树。
下面这篇博客讲的很清楚,我就不多讲,只说下自己的理解。
戳我学习
B树,B+树
二叉搜索树
- 左结点比根节点小,右结点比根节点大。
- 所有非叶子结点最多有两个儿子,每个节点只存一个key
- 如果所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么搜索性能逼近二分查找;但它比连续内存空间的二分查找的优点是,改变结构(插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销;
- 二叉查找树越是“矮胖”,也就是每层尽可能地被“塞满”(每个父节点均有两个子节点)时,查找效率越高。每层都被塞满时,查找效率最高,最高为O(log n)。当二叉查找树退化为单链表时,查找效率最低,最低为O(n)。
平衡二叉树
- 在二叉搜索树的基础上“平衡”。
- 任何结点的左右子树高度最多相差1.
- 当插入或移除一个结点时通常需要一次或多次左旋或右旋来保持平衡性。
B-树就是B树
- 是一种多路搜索树是m叉(m阶)的m>2.
- 定义任意非叶子结点最多只有M个儿子;且M>2;(即第一条)
- 每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)
- 非叶子结点的关键字个数=指向儿子的指针个数-1;(指针比关键字多一个)
- 所有叶子结点位于同一层,同一节点的关键字按升序排序。
- 除根结点以外的非叶子结点的儿子数为[M/2, M];
-
B-树的特性
-
关键字集合分布在整颗树中;
-
任何一个关键字出现且只出现在一个结点中;
-
搜索有可能在非叶子结点结束;
-
其搜索性能等价于在关键字全集内做一次二分查找;
-
( 有的书上说,非叶子结点最前面还有一个整型域表示节点中索引项个数);
-
B+树
-
其定义基本与B-树相同
-
非叶子结点的子树指针与关键字个数相同;
-
非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树
(B-树是开区间);
-
为所有叶子结点增加一个链指针;
-
所有关键字都在叶子结点出现;
-
B+的特性:
- 1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
- 不可能在非叶子结点命中;
- 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
- 更适合文件索引系统;
为什么说B+tree比B树更适合实际应用中操作系统的文件索引和数据库索引?
B+tree的磁盘读写代价更低 B+tree的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
(B+树非叶子节点就只是索引,并不包含实际数据,所以一次装入内存的结点数更多)我的理解
B+tree的查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
总而言之,B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。正是为了解决这个问题,B+树应运而生。B+树只要遍历叶子节点就可以实现整棵树的遍历,支持基于范围的查询,而B树不支持range-query这样的操作(或者说效率太低)。
为什么要对树进行平衡,B-树为什么要设计成多路?
因为平衡和设置成多路(有多个孩子)可以降低树的高度,这样可以优化查找效率。
既然路数越多,树的高度越低,那么B树为什么不设计成无数多路?
- 这样搜索树就退化成 有序数组,而B树是用在文件系统的索引等中。而文件系统和数据库的索引都是在硬盘上的,如果数据量太大,不一定能一次性加载到内存中。
- 有序数组更新太慢!
此时B树的多路存储就发挥作用了,每次加载树的一个结点,然后一步步的往下找。
而如果内存一次只能加载两个数,长的有序数组是无法一次加载进内存的。
如果我们把它组织成一个三路的B树,这样每个结点最多有两个数。这样每次加载一个结点就可以了。
当涉及到磁盘操作时,B树(多叉)比红黑树(二叉)更有效率,因为一个结点中的内容更多,减少了磁盘IO操作。
为什么B+树在数据库索引中用的比较多?
因为平时SELECT取的时候一般取的不是一条数据,而是相邻的多条数据。
如果是多条的话B树需要做局部的中序遍历,可能要跨层访问,而B+树都集中在叶子结点而且有链表结构很方便的可以把一段数据取出来。