B树介绍
- B树是一种平衡的多路搜索树,多用于硬盘,文件系统,数据库。
m阶B树性质
- 根节点下子节点数为 [2,m]
- 一个m阶B树的key数量x为: ⌈ m 2 ⌉ − 1 < = x < = m − 1 ⌈ \frac{m}{2}⌉ -1 <= x <= m -1 ⌈2m⌉−1<=x<=m−1
- 除了根结点和叶子结点外,一个m阶B树的childs数量y为: ⌈ m 2 ⌉ < = y < = m ⌈\frac{m}{2}⌉ <= y <= m ⌈2m⌉<=y<=m
- 三阶B树子节点个数: [ c e i l i n g ( 3 / 2 ) , 3 ] = [ 2 , 3 ] [ceiling(3/2) ,3] = [2,3] [ceiling(3/2),3]=[2,3] 每个节点可以有2~3个子节点 称为 二三树
- 四级B树子节点个数:
[
c
e
i
l
i
n
g
(
4
/
2
)
,
4
)
)
=
[
2
,
4
]
[ceiling(4/2) ,4)) = [2,4]
[ceiling(4/2),4))=[2,4] 每个节点可以有2~4个子节点
称为 二三四树
- 所有叶子节点都在,同一深度。
- 每个节点中的元素(key)从小到大排列。
- 每个元素(key)字左结点的值,都小于或等于该元素(key)。右结点的值都大于或等于该元素(key)。
B树能解决什么问题
不同容量的存储器,访问速度差异悬殊。
磁盘(ms级别) << 内存(ns级别), 100000倍
若内存访问需要1s,则一次外存访问需要一天
为了避免1次外存访问,宁愿访问内存100次…
所以将最常用的数据存储在最快的存储器中
普通的二叉树,如红黑树,AVL树,它们的应用场景,只适合在内存中直接读写,算法复杂度为
O
(
l
o
g
n
)
O(log_n)
O(logn)级别的,但是如果大量数据在硬盘中存储时,假设 你电脑里 有一百万个文件, 如果你要找 一个文件通过文件名那么 你通过 红黑树去找
l
o
g
2
1000000
=
20
log_{2}^{1000000}= 20
log21000000=20 那么 你每次 去硬盘取一个节点,连续的你需要去取最多20次 才能找到你所需要的文件。一般一次磁盘的IO时间在20 ~ 30 ms 左右,那么 整个 过程 大概也要500ms 左右吧,你可能说想 不到半秒 也不是很慢吗,但是你要知道 CPU在500ms的时间内能处理 几十亿次运算,所以 你电脑CPU再好也得折在磁盘IO上,普通二叉树的结构在对磁盘这种存储介质读取的时候 完全跟不上 CPU 和 内存的响应时间,会拖慢整个系统的响应速度,所以 为了解决这个问题
就产生了B树,原来每次搜索 我只能 排除一半数据的,B树内含 多个key这样的话,多个子节点,这样 可以大大减少磁盘IO访问的次数,还是一百万个数据,如果用100阶B树的话, 那么
l
o
g
100
1000000
=
3
log_{100}^{1000000}=3
log1001000000=3只需要最多 3次就能,找到所需要的数据。
上面我们讲到了为什么使用B树的一点好处,同时使用B树 同时根据 局部性原理
当一个数据被用到时,其附近的数据也通常会马上被使用。 通常读取一次硬盘 最小单位 大概在4k (取决于文件系统),也就是说 你读1B 和 读 4k花费的是一样的成本,前面讲到的红黑树 我们不能很好的利用 局部性原理,存储一个节点 可能只要不到1K的数据,那么 剩下的3k+ 数据 也就被浪费掉了,而B树就可以很好的利用局部性原理 ,假设存储一个key 和 下一个node指针 需要 20个字节,那么 我们就可以 计算出 1024 * 4 / 20 = 204 个索引值,就是 205阶B树。
我们先来 定义 基本的数据结构
#[derive(Debug)]
//B树节点
struct BtreeNOde<T>{
leaf:bool, //是否叶子
n:i32, //分支的数量
keys:Vec<T>,//存储keys
children:Vec<Box<BtreeNOde<T>>>,//指向自己的多个分支节点
}
#[derive(Debug)]
struct Btree<T> {
Root:Option<Box<BtreeNOde<T>>>,
branch:i32, //分支数
}
B树的搜索
B树的搜索较为 简单,我们只需要判断 当前这个数 是否大于前一个数小于当前数
如果满足条件,就 往下一层去搜索,直到搜索到 相等的结束,直到搜索到叶子节点结束搜索。
B树的插入
- 插入的节点必然添加在叶子节点。
- 如果插入节点包含m-1个keys 需要上溢处理。