1 意义
由于内外存的访问速度差距过大,相差5至6个数量级,在衡量算法的性能时,基本可以忽略对内存的访问,转而更多地关注对外存的访问次数。那么如何能有效地减少外存操作呢?
为此,需要利用磁盘之类的外部存储器的一类特性:就时间成本而言,读取物理地址连续的一千个字节,与读取单个字节几乎没有区别。既然外存在批量式访问时会增加效率,而树的各节点又不是连续存储的,那我们能不能使树的部分节点连续存储呢?
答案是可以的,由此产生了B树这种数据结构。
2 定义
B树也叫多路平衡搜索树,B树本质还是一颗二叉搜索树,还是需要遵循中序单调非降的规则。B树的节点、孩子均采用向量,即每个节点由n-1个关键码和n个分支组成,其中的n便为B树的阶次。
n阶B树亦n路平衡搜索树(n>2)有以下三种限制条件
- 节点中关键码的数取值范围 [⌈n/2⌉-1,n)
- 分支数取值范围 [⌈n/2⌉,n]
- 根节点取值范围 [1,n)
- 外部节点深度相同
下图便是4阶B树
3 查找
3.1 查找策略
B树的查找策略与二叉搜索树的查找策略相同,不过由于节点的结构不同,每层需要多次比较才能转入下一层
3.2 查找效率
由上可见,B树的查找操作主要有两步,一是将节点载入内存,二是在内存中对当前节点进行比较。鉴于内外存在访问速度上的巨大差异,后者的时间消耗可以忽略不计。所以B树的查找操作的效率取决于外存的访问次数,而访问次数不会超过O(h-1)次。由B树对节点最小取值的限制,可以证明M个数据的n阶B数的高度h=O(lognM),因此查找的时间复杂度为O(logn)
4 插入
4.1 插入策略
B树的插入策略与二叉搜索树的常规策略基本相同。插入完成后判断节点中的关键码个数是否超出,若超出则分裂上溢
。分裂上溢的具体操作是,将第n/2个关键码上溢到父节点,子节点被分成两个,不难证明,分裂后的两个子代都满足关键码个数≥⌈n/2⌉-1
4.2 实例分析
(a) 是一个4阶B树
(b) 插入23
(c1)插入29
(c2)关键码个数超出,分裂,中间关键码23上溢
(d1)插入45
(d2)关键码个数超出,分裂,中间关键码45上溢
(d3)关键码个数超出,分裂,中间关键码36上溢
(e1)插入87
(e2)关键码个数超出,分裂,中间关键码84上溢
(e3)关键码个数超出,分裂,中间关键码84上溢
(e4)根节点关键码个数超出,分裂,中间关键码53上溢,成为新根
4.3 插入效率
若将B树的阶次m视作常数,则关键码的移动和复制操作所需时间都可以忽略。至于上溢,每次递归实例均只需常数时间,递归层数不超过B树高度,由此可知,每次插入操作都在O(logn)时间内完成
5 删除
5.1 删除策略
B树的删除策略与二叉搜索树的常规策略基本相同,只不过删除后可能导致节点的关键码个数不足,处理情况可分为两种。
- 兄弟节点关键码个数充足,将兄弟节点离父节点最近的一个关键码提升,而父节点的关键码下溢至缺少关键码的子代
- 兄弟节点关键码刚好为⌈n/2⌉-1时,向父节点借一个关键码,将两个子节点合并
5.2 实例分析
(a) 删除41
(b1)删除53
(b2)53与直接后继交换位置,然后删除
(b3)删除75
(c1)关键码个数不足,情况一,从兄弟节点中借出关键码,旋转填补
(c2)删除84
(d1)关键码个数不足,情况二,父节点下溢一个关键码,且两子节点合并
(d2)删除51
(e1)关键码个数不足,情况二,父节点下溢一个关键码,且两子节点合并
(e2)关键码个数不足,情况二,父节点下溢一个关键码,且两子节点合并
(e3)根节点关键码不足
(e4)根下溢,新根出现
5.3 删除效率
与插入操作同理,在存有N个关键码的n阶B树中的每次关键码删除操作,都可以在O(logN)时间内完成。
该文插图均来自邓俊辉老师的《数据结构与算法》