B树

介绍

B树的目的为了硬盘快速读取数据(降低IO操作次树)而设计的一种平衡的多路查找树。目前大多数据库及文件索引,都是使用B树或变形来存储实现。

目录

  1. 为什么B树效率高
  2. B树存储
  3. B树缺点

为什么B树效率高

在大规模数据存储操作中,由于无法一次性加载到内存里。所以避免不了发生内外存交换。所以次数越少,效率表现也越高。

来看下面这张图:

0

这是个典型的b树结构,初始因子为1000,高度仅为3的b树,就可以存储1002001000的数据了。

假设要查询最后一个数据:

  • 从硬盘加载根节点搜索,IO一次。
  • 根据根节点的指针信息,去加载第二层的节点, IO一次。
  • 重复2,IO一次。

IO只用了3次,就查询了需要的数据,所以说B树效率是非常高的。

B树的节点,在硬盘里表现为:柱面里的页(page)或盘块(block) ,如果把索引持久化到内存,只需要一次就够了。

B树的高效的前提是数据已排序。

B树结构

  

这是B树存储在硬盘的逻辑结构图。

其中根节点中17,35在称为关键字(key) ,实际中往往附带更多复杂类型数据。

可以看出一个节点包含 keys  ChildNotePointer  2部分信息。

 

根据这张图介绍下b树的基础定义:

这是颗5阶B树的图,阶简写m。

    1:树中每个结点最多含有m个子节点(m>=2)。 

    2:每个内节点至少 [ceil(m / 2)] 个子节点。  内节点即非根节点非页子节点,也可以叫中间节点。

    3: 关键字key的数量   [ceil(m / 2)-1]<= n <= m-1,关键字按递增排序。

    6: 每个叶节点具有相同的深度,即树的高度h,而且不包含关键字信息。

上图也可称为最小度数为3的b树,(degree) ,简写t。  

              t其实是上面第二条定义中 [ceil(m / 2)] 的值,即t=[ceil(m/2)], 3=ceil(5/2) 。 

    1:每个非根节点至少有t-1个关键字,非根内节点至少有t个子节点。 t称为度数(degree),t>=2  。

 .  2:每个节点至多有2t-1关键字,每个内节点最多有2t个子节点。

    3:每个叶节点具有相同的深度,即树的高度h,而且不包含关键字信息。

度和阶都是描述子节点的数量的。

算法导论译版中是用度来描述的。

数据结构与算法分析是用阶来描述,网上大多也是。

下面简单的描述实现逻辑。

搜索:从根节点搜索,找到返回,找不到递归子节点。一直搜索到叶子节点,找到返回,找不到则说明key不存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
//伪代码
entry BTreeSearch(node, key) {
    if(node == null)
           return null;
    for(int i = 0; i < node.keys.length; i++)
    {
        if(node.keys[i] == key)
               return node.data[i];
    }
    return BTreeSearch(ChildrenNode[i].node,key);
}
 
var  entry = BTreeSearch(root, my_key);

插入:根节点插入,不满直接插入。节点满进行分裂,再满递归分裂。

删除:查询到节点,然后进行删除操作,不满足B数节点的定义则进行节点合并。

更新:查询到子节点,更新数据。

B树缺点

从上面的得知,在查询单条数据是非常快的。但如果范围查的话,b树每次都要从根节点查询一遍。

所以在实际应用中,往往采用b树的变形,b+树来存储,只有叶子节点存储数据,每个叶子节点都指向下一个。



06-14
### B的数据结构和实现 B是一种自平衡的搜索,广泛应用于数据库和文件系统中。其设计目的是为了减少磁盘访问次数,提高数据检索效率。B的每个节点可以包含多个关键字和子节点指针,且所有叶子节点位于同一层。 #### 1. B的基本定义 B由节点组成,每个节点包含一组关键字和一组子节点指针。对于一个最小度数为 \(t\) 的B,有以下特性[^2]: - 每个节点最多包含 \(2t-1\) 个关键字。 - 根节点至少包含一个关键字。 - 非根节点至少包含 \(t-1\) 个关键字。 - 如果某个节点是非叶子节点,则它包含 \(n\) 个关键字和 \(n+1\) 个子节点指针,其中 \(t \leq n \leq 2t-1\)。 - 所有叶子节点都在同一层。 #### 2. B节点的结构 在代码实现中,B的节点通常定义如下[^2]: ```c typedef int KEY_VALUE; typedef struct _btree_node { KEY_VALUE *keys; // 关键字数组 struct _btree_node **childrens; // 子节点指针数组 int num; // 当前节点的关键字数量 int leaf; // 是否为叶子节点 } btree_node; ``` #### 3. B的操作 B的主要操作包括插入、删除和查找。以下是这些操作的基本描述: ##### (1) 插入操作 当向B中插入一个新关键字时,可能需要分裂节点以保持的平衡。具体步骤如下: - 如果根节点满,则创建一个新的根节点,并将原根节点作为其子节点。 - 自顶向下查找合适的位置插入关键字。 - 如果某节点已满,则将其分裂为两个节点,并将中间关键字上移至父节点。 ##### (2) 删除操作 删除关键字时,可能需要合并或重新分配节点中的关键字以保持的平衡。具体步骤如下: - 查找待删除的关键字。 - 如果关键字存在于非叶子节点,则用其前驱或后继替换该关键字。 - 如果删除导致某节点的关键字数量低于下限,则从兄弟节点借关键字,或与兄弟节点合并。 ##### (3) 查找操作 查找操作通过比较关键字和节点中的值,逐步缩小搜索范围。具体步骤如下: - 从根节点开始,逐层向下查找。 - 在当前节点中查找目标关键字,如果找到则返回;否则进入相应的子节点继续查找。 #### 4. 示例代码 以下是一个简单的B插入操作的伪代码示例: ```c void btree_insert_nonfull(btree_node *node, KEY_VALUE key) { int i = node->num - 1; if (node->leaf) { // 如果是叶子节点 while (i >= 0 && node->keys[i] > key) { node->keys[i + 1] = node->keys[i]; i--; } node->keys[i + 1] = key; node->num++; } else { // 如果是非叶子节点 while (i >= 0 && node->keys[i] > key) { i--; } i++; if (node->childrens[i]->num == 2 * t - 1) { // 如果子节点已满 split_child(node, i); if (key > node->keys[i]) { i++; } } btree_insert_nonfull(node->childrens[i], key); } } ``` #### 5. B与B+的对比 尽管B功能强大,但在实际应用中,B+更为常用。B+对B进行了优化,主要改进点包括[^1]: - 所有关键字都存储在叶子节点中,便于范围查询。 - 叶子节点之间通过链表连接,支持顺序访问。 - 非叶子节点仅用于索引,不存储实际数据。 ### 总结 B是一种高效的多路平衡搜索,适用于大规模数据存储和检索场景。其核心在于通过节点分裂和合并保持的平衡性,从而确保操作的时间复杂度为 \(O(\log n)\)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值