B树详解及其C语言实现

news/2025/2/9 8:22:32 标签: b树, 数据结构, c语言, 算法

目录

一、B树的基本原理

二、B树操作过程图形化演示

三、B树的应用场景

四、C语言实现B树及示例

 五、代码执行结果说明

六、应用实例:文件系统目录索引

七、总结


一、B树的基本原理

B树(B-Tree) 是一种自平衡的树数据结构,专为高效处理磁盘或数据库中的大量数据而设计。它的核心特性是每个节点可以包含多个键和子节点指针,通过控制每个节点的最小/最大键数量,确保树的高度始终为对数级别。

B树的定义(以m阶B树为例)

     B树是一种多路搜索树,也被称为平衡多路查找树。与二叉搜索树不同,B树的每个节点可以拥有多个子节点和键值。B树的每个节点包含键值集合、子节点指针集合和一个平衡因子。键值集合按照从小到大的顺序排列,子节点指针集合按照左子节点、右子节点的顺序排列。平衡因子用于衡量节点的平衡性。

  1. 节点容量:B树的阶(Order)或分支因子(Branch Factor)通常用字母m表示,它定义了节点可以拥有的最大子节点数(即m个子节点)。因此,一个节点最多可以有m-1个键值。最少包含 m/2−1 个键(根节点除外)
  2. 子节点数:每个非叶子节点最多有 m个子节点,最少有 m/2个子节点
  3. 有序性:所有键在节点内按升序排列

  4. 平衡性

    • B树通过保持树的平衡性,确保所有叶子节点都在同一层,从而实现了高效的查找、插入和删除操作。
    • 这种平衡性确保了所有查找、插入和删除操作的时间复杂度都是O(log n),其中n是树中元素的数量。
  5. 操作原理

    • 查找操作:从根节点开始,通过比较要查找的键与节点中的键,决定是继续在左子树还是右子树中搜索,直到找到目标键或到达叶子节点为止。
    • 插入操作:找到合适的叶子节点,然后将新键插入该节点。如果插入后节点中的键的数量超过了m-1,则节点会分裂成两个节点,并将中间的键提升到父节点。如果父节点也满了,则继续向上分裂,直到根节点。如果根节点也分裂,则创建一个新的根节点,并包含分裂出的中间键。
    • 删除操作:找到包含要删除键的节点,并从节点中移除该键。如果删除后节点中的键的数量少于要求的最小数量(⌈m/2⌉-1),则需要重新分配或合并节点。重新分配通常是从兄弟节点借键,合并则是将当前节点与兄弟节点合并,并可能将父节点中的键下移。

二、B树操作过程图形化演示

示例:构建一个3阶B树(每个节点最多2个键,3个子节点)

插入序列:[10, 20, 5, 6, 12, 30, 7, 17]

1. 插入10(根节点):
[10]

2. 插入20(直接填充根节点):
[10, 20]

3. 插入5(根节点已满,触发分裂):
      [10]
     /   \
  [5]   [20]

4. 插入6(插入到左子节点):
      [10]
     /   \
  [5,6] [20]

5. 插入12(插入右子节点,无需分裂):
      [10]
     /   \
  [5,6] [12,20]

6. 插入30(右子节点分裂):
      [10,20]
     /   |   \
  [5,6] [12] [30]

7. 插入7(左子节点分裂,触发根节点更新):
        [6,10,20]
       /   |   |   \
    [5] [7] [12] [30]

三、B树的应用场景

场景应用原理
数据库索引

B树保持数据有序,支持高效范围查询(如MySQL的InnoDB引擎使用B+树变种)

  • B树在数据库中被广泛用于索引结构,以加速数据的检索速度。由于B树能够保持平衡性,使得所有叶子节点到根节点的路径长度相近,从而保证了搜索的效率。
  • 在数据库中,索引是帮助快速查找数据的数据结构。通过B树,数据库系统可以快速定位数据的存储位置,提高数据访问的速度。
文件系统

NTFS、ReiserFS等文件系统用B树管理文件和目录的元数据

  • B树也被广泛应用于文件系统中,用于实现目录结构和文件分配表,以管理文件的存储和访问。
  • 文件系统通常需要频繁地进行查找和检索文件,而B树的平衡性和高度平衡特性使得这一过程更为高效。
  • B树能够减少磁盘I/O操作的次数,从而显著提高数据存取的效率。这对于大型文件系统来说尤为重要。
内存受限环境通过减少树高度降低内存访问次数
网络路由表快速查找IP地址对应的路由信息
外部排序

在外部排序中,由于数据量太大,无法一次性装入内存,因此需要使用磁盘等外部存储设备。B树可以作为外部排序过程中的一个关键数据结构,帮助实现多路归并排序,提高排序的效率。

四、C语言实现B树及示例

#include <stdio.h>
#include <stdlib.h>
#define M 3  // B树阶数
#define MAX_KEYS (M-1)
#define MIN_KEYS (M/2 - 1)

typedef struct BTreeNode {
    int keys[MAX_KEYS];
    struct BTreeNode *children[M];
    int num_keys;
    int is_leaf;
} BTreeNode;

BTreeNode* create_node(int is_leaf) {
    BTreeNode* node = (BTreeNode*)malloc(sizeof(BTreeNode));
    node->num_keys = 0;
    node->is_leaf = is_leaf;
    for (int i = 0; i < M; i++) node->children[i] = NULL;
    return node;
}

void split_child(BTreeNode *parent, int index) {
    BTreeNode *child = parent->children[index];
    BTreeNode *new_node = create_node(child->is_leaf);
    
    // 新节点获取后半部分键
    new_node->num_keys = MIN_KEYS;
    for (int i = 0; i < MIN_KEYS; i++) 
        new_node->keys[i] = child->keys[i + MIN_KEYS + 1];
    
    // 移动子节点指针(若非叶子节点)
    if (!child->is_leaf) {
        for (int i = 0; i <= MIN_KEYS; i++)
            new_node->children[i] = child->children[i + MIN_KEYS + 1];
    }
    child->num_keys = MIN_KEYS;
    
    // 将中间键提升到父节点
    for (int i = parent->num_keys; i > index; i--)
        parent->children[i + 1] = parent->children[i];
    parent->children[index + 1] = new_node;
    
    for (int i = parent->num_keys - 1; i >= index; i--)
        parent->keys[i + 1] = parent->keys[i];
    parent->keys[index] = child->keys[MIN_KEYS];
    parent->num_keys++;
}

void insert_non_full(BTreeNode *node, int key) {
    int i = node->num_keys - 1;
    
    if (node->is_leaf) {
        while (i >= 0 && key < node->keys[i]) {
            node->keys[i + 1] = node->keys[i];
            i--;
        }
        node->keys[i + 1] = key;
        node->num_keys++;
    } else {
        while (i >= 0 && key < node->keys[i]) i--;
        i++;
        if (node->children[i]->num_keys == MAX_KEYS) {
            split_child(node, i);
            if (key > node->keys[i]) i++;
        }
        insert_non_full(node->children[i], key);
    }
}

void insert(BTreeNode **root, int key) {
    if ((*root)->num_keys == MAX_KEYS) {
        BTreeNode *new_root = create_node(0);
        new_root->children[0] = *root;
        *root = new_root;
        split_child(new_root, 0);
        insert_non_full(new_root, key);
    } else {
        insert_non_full(*root, key);
    }
}

// 打印B树(中序遍历)
void print_tree(BTreeNode *node, int level) {
    printf("Level %d: ", level);
    for (int i = 0; i < node->num_keys; i++)
        printf("%d ", node->keys[i]);
    printf("\n");
    
    if (!node->is_leaf) {
        for (int i = 0; i <= node->num_keys; i++)
            if (node->children[i]) print_tree(node->children[i], level + 1);
    }
}

int main() {
    BTreeNode *root = create_node(1); // 初始为叶子节点
    
    int keys[] = {10, 20, 5, 6, 12, 30, 7, 17};
    for (int i = 0; i < sizeof(keys)/sizeof(int); i++) {
        insert(&root, keys[i]);
        printf("After inserting %d:\n", keys[i]);
        print_tree(root, 0);
        printf("----------------\n");
    }
    return 0;
}

 五、代码执行结果说明

输出示例(部分):

After inserting 10:
Level 0: 10 
----------------
After inserting 20:
Level 0: 10 20 
----------------
After inserting 5:
Level 0: 10 
Level 1: 5 
Level 1: 20 
----------------
...

代码特性:

  1. 动态分裂:当节点满时自动分裂

  2. 递归插入:通过insert_non_full函数确保插入路径上的节点都有空间

  3. 层级打印:可视化展示B树结构

六、应用实例:文件系统目录索引

typedef struct {
    char filename[256];
    long offset;
} FileRecord;

// 在B树节点中存储文件记录
BTreeNode* create_file_index() {
    return create_node(1); // 创建叶子节点
}

void index_file(BTreeNode **root, const char *filename, long offset) {
    int hash_key = 0;
    for (int i = 0; filename[i]; i++) hash_key += filename[i];
    insert(root, hash_key); // 实际应存储FileRecord结构
}

该实现可用于构建文件系统索引,通过文件名哈希快速定位文件物理位置。

七、总结

综上所述,B树作为一种高效的数据结构,在数据库系统、文件系统以及外部排序等领域具有广泛的应用前景。其平衡性和高度平衡的特性使得B树在处理大量数据时能够保持稳定的性能,从而满足了各种复杂和多样化的数据存储和检索需求。


http://www.niftyadmin.cn/n/5845827.html

相关文章

数据结构--八大排序算法

1. 直接插入排序 当插入第 i(i>1) 个元素时&#xff0c;前面的 array[0],array[1],…,array[i-1] 已经排好序&#xff0c;此用 array[i] 的排序码与 array[i-1],array[i-2],… 的排序码顺序进行比较&#xff0c;找到插入位置即将 array[i] 插入&#xff0c;原来位置上的元素…

【论文翻译】DeepSeek-V3论文翻译——DeepSeek-V3 Technical Report——第一部分:引言与模型架构

论文原文链接&#xff1a;DeepSeek-V3/DeepSeek_V3.pdf at main deepseek-ai/DeepSeek-V3 GitHub 特别声明&#xff0c;本文不做任何商业用途&#xff0c;仅作为个人学习相关论文的翻译记录。本文对原文内容直译&#xff0c;一切以论文原文内容为准&#xff0c;对原文作者表示…

LVGL4种输入设备详解(触摸、键盘、实体按键、编码器)

lvgl有触摸、键盘、实体按键、编码器四种输入设备 先来分析一下这四种输入设备有什么区别 &#xff08;1&#xff09;LV_INDEV_TYPE_POINTER 主要用于触摸屏 用到哪个输入设备保留哪个其他的也是&#xff0c;保留触摸屏输入的任务注册&#xff0c;其它几种种输入任务的注册&…

Python的秘密基地--[章节13] Python 数据分析与可视化

第13章&#xff1a;Python 数据分析与可视化 在大数据时代&#xff0c;数据分析与可视化是至关重要的技能。Python 提供了多个强大的库&#xff0c;如 NumPy、Pandas、Matplotlib 和 Seaborn&#xff0c;用于数据处理、分析和可视化。本章将介绍如何使用 Python 进行数据分析&…

FPGA高端项目:图像采集+UltraScale GTH光编码+UDP图传架构,高速接口转网络视频传输,提供工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案我这里已有的以太网方案 3、工程详细设计方案工程设计原理框图输入Sensor之-->OV5640摄像头动态彩条视频数据组包基于UltraScale…

SpringBoot3与MyBatis-Plus

4.1 介绍 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个基于 MyBatis 的增强工具&#xff0c;提供通用 CRUD 操作、代码生成器、条件构造器、分页插件等功能&#xff0c;简化开发流程&#xff0c;提升效率。 4.2 特点 无侵入&#xff1a;只做增强不做修改&#xff0c;与…

ssti学习笔记(服务器端模板注入)

目录 一&#xff0c;ssti是什么 二&#xff0c;原理 所谓模板引擎&#xff08;三列&#xff0c;可滑动查看&#xff09; 三&#xff0c;漏洞复现 1&#xff0c;如何判断其所属的模板引擎&#xff1f; 2&#xff0c;判断清楚后开始注入 &#xff08;1&#xff09;Jinja2&a…

MySQL第五次作业

根据图片内容完成作业 1.建表 &#xff08;1&#xff09;建立两个表:goods(商品表)、orders(订单表) mysql> create table goods( -> gid char(8) primary key, -> name varchar(10), -> price decimal(8,2), -> num int); mysql> create t…