AVL树和红黑树都是用作内查找的数据结构,即被查询的数据集合不大,可以放在内存中;B树和B+树是用作外查询的数据结构,其数据是存储在外村中的。
B树中所有节点的孩子节点的最大值称为B树的阶,通常用m表示。(节点最大分支数就是阶数)从查找效率来说,要求m≥3
B数的要求如下:
树中每个节点最多有m颗子树
若根节点不是叶子节点,则根节点至少有两个子树(分支)
除根节点外,所有非叶子结点最少有[m/2]颗子树
关键字数量n规定:[m/2]-1≤n≤m-1
每个节点上的关键字按照升序排列,子树插在关键字的间隙里(所以子树个数大于关键字个数)
除了根节点外每个节点至少有[m/2]个分叉,至少[m/2]-1个关键字 为什么?
主要原因是为了让树变得矮一些、同时让每个节点更满(如果一个节点可以放100个关键字,实际只放1个,是不是很浪费),这样查询的时候查询次数就会变少。如果不限制至少的分叉个数,当一个节点只有一个分叉(或者两个)、每个接节点只放1个关键字的时候,树的高度就会很高,影响查找速度,这样规定的目的就是为了把树变矮一些,节点关键字数变得饱满一些。
若根节点不是叶子节点,则根节点至少有两个子树(分支) 为什么?
最少两个分支就是为了让B树平衡,如果不限定,那么从根节点开始一直一个分支下去,整个B树就会非常高(防止树向一边偏),查找效率会很低
抽取m阶B树的核心特性:
根节点的子树数n:2≤n≤m,关键字数:1≤n≤m-1
其他节点子树数:[m/2]≤n≤m,关键字数:[m/2]-1≤n≤m-1
对任一节点,其所有子树高度都相同(叶子结点都在最后一层)
每个节点关键字的值:子树0<关键字1<子树1<关键字2<子树2<关键字3....
(一个节点的关键字是升序排列的,对于插入在关键字缝隙的子树,整个子树的所有值都小于其右边关键字的值)
插入:对于插入来说,首先要找到要插入的节点,根据B数的关键字大小的规定(左小右大),很容易找到插入位置。找到插入位置后,如果该节点的关键字数量没达到最大值m-1,直接插入;如果已满,需要进行分裂。具体分裂原则是:将该节点插入后,以该节点的中间关键字为中心(不包括中间关键字),分为两个节点,然后将中间的节点向上移(移到父节点上),如果父节点满了,再进行分裂,以此类推。
删除:如果删除的是叶子结点,且该节点的关键字数大于最小约束值,直接删除;如果关键字数等于最小约束值,那么需要向兄弟节点借。如果要删除的是非叶子结点,需要将该节点转化到叶子结点上,然后利用删除叶子结点上关键字方法删除即可。
B+树比B树更适合实际应用中操作系统的文件索引和数据库索引,相比于B树,B+树还有如下特征:
每个节点最多m颗子树,最多m个关键字(每个关键字对应一颗子树,不再是B树那种插缝的了)
每个节点的关键字值是对应的子树的最大值(也就是这个关键字值是子树中最大的那个,所以B+树的关键字值是可以重复的)
叶子结点层:每个节点使用链表连接起来的,链表的有个头结点sqt,通过sqt头结点可以顺序遍历叶子结点层
B+树的叶子节点层上包含了所有关键字,即其他非叶子节点中的关键字包含在叶子结点中,而B树中关键字时不重复的。
m阶B树 | m阶B+树 | |
---|---|---|
关键字与分叉(子树) | n个关键字对应n+1个分叉 | n个关键字对应n个分叉 |
节点包含的信息 | 所有节点都包含记录信息 | 只有叶子结点层的节点才包含记录信息 |
查找方式 | 检索树 | 检索树+链表 |
相同点 | 对节点有分支数和关键字数要求(保证平衡和饱满) | 对节点有分支数和关键字数要求(保证平衡和饱满) |
【说明】
由于B树节点包含记录信息(指存的字符串、文件信息、用户信息等),所以B树不适合做组织文件的数据结构,会使得节点数据过大
B+树查找:只有查找到叶子层才算查找结束,如果要找的元素在叶子层没有,那就是查找失败;而B树在某个节点查找到对应的关键字就结束了
为什么使用B+树用于文件索引和数据库索引:
因为每个节点不存记录,只有叶子层存储记录,所以每个节点能存更多的关键字,而操作系统在检索的时候先把一个节点作为一个块读入内存然后进行查找,找到对应的子树地址后,再把子树节点按块读取到内存,以此类推;
因为操作系统中块大小是固定的(比如1K),如果像B树那样,每个节点即存记录又存关键字,那么关键字数量大大减少,不如B+树只存关键字来得多,所以检索起来B+树读磁盘次数少,效率更高,查找起来更快。
B+树的插入和删除:与B树操作类似。
【参考:王道计算机考研 数据结构_哔哩哔哩_bilibili 】