c语言实现b树

简介: 这篇文章介绍了B树的概念、特点和应用场景,并提供了一个C语言实现的B树数据结构示例代码,包括节点定义、创建新节点、分裂节点、插入关键字和打印B树等函数。

概述:B 树(B-tree)是一种自平衡的搜索树数据结构,广泛应用于数据库和文件系统等领域。它的设计旨在提供一种高效的插入、删除和查找操作,同时保持树的平衡,确保各个节点的深度相差不大。

B 树的特点包括:

  1. 平衡性: 所有叶子节点到根节点的路径长度相等,确保在查找、插入和删除等操作时,各个节点的访问次数相对均衡,提高了性能。

  2. 多路搜索: B 树每个内部节点可以有多个子节点,这是与二叉搜索树的主要区别。每个节点包含多个关键字和对应的子树,这样可以减少树的高度,提高检索效率。

  3. 节点存储多个关键字: 每个节点可以存储多个关键字,而不仅仅是一个。这样可以提高每个节点的利用率,减少磁盘 I/O 次数。

  4. 自调整: 在插入和删除操作时,B 树会自动调整其结构以保持平衡。这通过节点分裂和合并的方式来实现。

B 树的阶数(order)是一个重要的概念,它表示一个节点最多可以包含的子节点数(或关键字数)。通常,B 树的阶数会根据具体的使用场景进行调整,以满足性能和空间的需求。

B 树广泛应用于数据库系统中,作为索引结构,因为它能够高效地支持范围查询、插入和删除操作。同时,B 树也被用于文件系统,以加速文件的检索和访问。

完整代码:

#include <stdio.h>
#include <stdlib.h>

// B 树的阶数
#define ORDER 3

// B 树节点结构
typedef struct BTreeNode {
    int leaf; // 是否是叶子节点
    int n;    // 当前关键字数量
    int keys[ORDER - 1]; // 关键字数组
    struct BTreeNode* child[ORDER]; // 子节点数组
} BTreeNode;

// 创建新节点
BTreeNode* createNode() {
    BTreeNode* newNode = (BTreeNode*)malloc(sizeof(BTreeNode));
    newNode->leaf = 1;
    newNode->n = 0;
    for (int i = 0; i < ORDER - 1; ++i) {
        newNode->keys[i] = 0;
    }
    for (int i = 0; i < ORDER; ++i) {
        newNode->child[i] = NULL;
    }
    return newNode;
}

// 分裂节点
void splitChild(BTreeNode* parent, int index, BTreeNode* child) {
    BTreeNode* newNode = createNode();
    newNode->leaf = child->leaf;
    newNode->n = ORDER / 2 - 1;

    // 将 child 中的一半关键字移动到 newNode 中
    for (int i = 0; i < ORDER / 2 - 1; ++i) {
        newNode->keys[i] = child->keys[i + ORDER / 2];
    }

    // 如果 child 不是叶子节点,将一半子节点移动到 newNode 中
    if (!child->leaf) {
        for (int i = 0; i < ORDER / 2; ++i) {
            newNode->child[i] = child->child[i + ORDER / 2];
            child->child[i + ORDER / 2] = NULL;
        }
    }

    // 调整 child 的关键字数量
    child->n = ORDER / 2 - 1;
    newNode->n = ORDER / 2 - 1;

    // 将 parent 中的一个关键字和 newNode 相关的子节点插入到 parent 中
    for (int i = parent->n; i > index; --i) {
        parent->keys[i] = parent->keys[i - 1];
        parent->child[i + 1] = parent->child[i];
    }
    parent->keys[index] = child->keys[ORDER / 2 - 1];
    parent->child[index + 1] = newNode;

    // 调整 parent 的关键字数量
    parent->n++;

    // 将 parent 中的一个关键字插入到 child 中
    child->keys[ORDER / 2 - 1] = 0;
    child->child[ORDER / 2] = NULL;
}

// 插入非满节点
void insertNonFull(BTreeNode** root, int key) {
    BTreeNode* node = *root;
    int i = node->n - 1;

    // 如果是叶子节点,直接插入
    if (node->leaf) {
        while (i >= 0 && key < node->keys[i]) {
            node->keys[i + 1] = node->keys[i];
            i--;
        }
        node->keys[i + 1] = key;
        node->n++;
    } else {
        // 如果是内部节点,找到要插入的子节点
        while (i >= 0 && key < node->keys[i]) {
            i--;
        }
        i++;

        // 如果子节点已满,先分裂再插入
        if (node->child[i]->n == ORDER - 1) {
            splitChild(node, i, node->child[i]);
            if (key > node->keys[i]) {
                i++;
            }
        }

        // 递归插入子节点
        insertNonFull(&(node->child[i]), key);
    }
}

// 插入关键字
void insert(BTreeNode** root, int key) {
    BTreeNode* node = *root;

    // 如果树为空,创建一个新节点作为根
    if (!node) {
        *root = createNode();
        (*root)->keys[0] = key;
        (*root)->n = 1;
        return;
    }

    // 如果根节点已满,先分裂再插入
    if (node->n == ORDER - 1) {
        BTreeNode* newRoot = createNode();
        newRoot->leaf = 0;
        newRoot->child[0] = *root;
        *root = newRoot;
        splitChild(newRoot, 0, node);
        insertNonFull(root, key);
    } else {
        insertNonFull(root, key);
    }
}



// 打印 B 树
void printBTree(BTreeNode* node, int level) {
    if (node) {
        printf("Level %d: ", level);
        for (int i = 0; i < node->n; ++i) {
            printf("%d ", node->keys[i]);
        }
        printf("\n");

        if (!node->leaf) {
            for (int i = 0; i <= node->n; ++i) {
                printBTree(node->child[i], level + 1);
            }
        }
    }
}

int main() {
    BTreeNode* root = NULL;

    // 插入一些关键字
    insert(&root, 3);
    insert(&root, 7);
    insert(&root, 2);
    insert(&root, 9);
    insert(&root, 1);
    insert(&root, 6);
    insert(&root, 4);
    insert(&root, 5);
    insert(&root, 8);

    // 打印 B 树
    printBTree(root, 0);

    return 0;
}

首先定义了B树的阶数ORDER为3,即每个节点最多可以有3个子节点。然后定义了一个B树节点的结构体,包含以下成员:

  • leaf:表示该节点是否为叶子节点,如果是叶子节点则为1,否则为0。
  • n:表示当前节点中关键字的数量。
  • keys:关键字数组,用于存储节点中的关键字。
  • child:子节点数组,用于存储指向子节点的指针。

接下来实现了创建新节点的函数createNode(),用于分配内存并初始化节点的成员变量。

然后实现了分裂节点的函数splitChild(),用于将一个节点分裂成两个节点。分裂过程包括将原节点的一半关键字移动到新节点中,并将原节点的一半子节点移动到新节点中。同时调整原节点的关键字数量和新节点的关键字数量。最后将原节点中的一个关键字和与新节点相关的子节点插入到原节点中,并调整原节点的关键字数量。

接着实现了插入非满节点的函数insertNonFull(),用于在非满节点中插入关键字。如果节点是叶子节点,则直接插入关键字;如果节点是内部节点,则找到要插入的子节点,如果子节点已满,则先分裂再插入。然后递归插入子节点。

最后实现了插入关键字的函数insert(),用于向B树中插入关键字。如果树为空,则创建一个新节点作为根;如果根节点已满,则先分裂再插入。然后调用insertNonFull()函数插入关键字。

最后实现了打印B树的函数printBTree(),用于按层次遍历的方式打印B树的结构。

在main()函数中,创建了一个空的根节点root,然后插入了一些关键字,并调用printBTree()函数打印B树的结构。

目录
相关文章
|
9月前
|
存储 分布式数据库 数据库
C语言 B树的分析与实现
本文主要说明了B树的概念、应用以及如何用C语言实现B树。
130 1
|
1月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
69 23
|
2天前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
10 1
一文彻底搞清楚C语言的函数
|
1月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
75 15
|
1月前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
63 24
|
1月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
70 16
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
39 3
|
1月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
24 2
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
54 1
|
2月前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
100 10