【数据结构】B树

B树

B树(B-tree)是一种自平衡的多路搜索树,它被广泛用于文件系统、数据库索引和其他需要快速查找、插入和删除操作的大数据结构中。B树在节点上可以包含多个键和多个子节点,这使得它在磁盘存储和数据操作方面非常高效。

B树的特性

  • 每个节点可以包含多个键和多个子节点。
  • 所有叶子节点位于相同的深度,保持树的平衡。
  • 每个节点至少包含 ceil(m/2) - 1 个键和 ceil(m/2) 个子节点,最大包含 m - 1 个键和 m 个子节点。
  • B树的深度较小,可以减少磁盘I/O操作,提高查找速度。
  • 所有键都是有序的,使得B树支持快速查找、插入和删除操作。

B树的操作

B树提供了基本的操作,包括查找、插入和删除操作。下面是这些操作的C语言实现。

定义节点结构

首先,我们定义B树的节点结构。节点包含多个键的数组 keys,多个子节点的数组 children,以及一个表示当前键数目的字段 numKeys

#define MAX_KEYS 4  // 最大键数目

typedef struct BTreeNode {
    int keys[MAX_KEYS];  // 键数组
    struct BTreeNode *children[MAX_KEYS + 1];  // 子节点数组
    int numKeys;  // 当前键数目
    int isLeaf;  // 是否是叶子节点
} BTreeNode;

在上面的代码中,我们定义了B树节点的最大键数目为4,因此一个节点最多可以包含 4 - 1 = 3 个键和 4 个子节点。

查找操作

查找操作在B树中查找特定值。查找过程是递归的,通过比较键的值和目标值来选择正确的子节点。

BTreeNode* search(BTreeNode* node, int value) {
    int i = 0;
    // 查找键的正确位置
    while (i < node->numKeys && value > node->keys[i]) {
        i++;
    }

    // 如果找到了目标键
    if (i < node->numKeys && value == node->keys[i]) {
        return node;
    }

    // 如果是叶子节点
    if (node->isLeaf) {
        return NULL;
    }

    // 递归查找子节点
    return search(node->children[i], value);
}

查找操作递归地遍历树,根据键的值选择正确的子节点进行查找。

插入操作

插入操作在B树中插入一个新的值。如果当前节点已满,则需要进行分裂操作。

// 分裂满的节点
void splitChild(BTreeNode *parent, int index, BTreeNode *child) {
    // 创建新的节点
    BTreeNode *newNode = (BTreeNode *)malloc(sizeof(BTreeNode));
    newNode->isLeaf = child->isLeaf;
    newNode->numKeys = MAX_KEYS / 2;

    // 复制后一半的键和子节点
    for (int j = 0; j < MAX_KEYS / 2; j++) {
        newNode->keys[j] = child->keys[j + MAX_KEYS / 2];
    }
    if (!child->isLeaf) {
        for (int j = 0; j <= MAX_KEYS / 2; j++) {
            newNode->children[j] = child->children[j + MAX_KEYS / 2];
        }
    }

    // 更新当前节点和子节点的键数目
    child->numKeys = MAX_KEYS / 2 - 1;

    // 插入新节点到父节点
    for (int j = parent->numKeys; j >= index + 1; j--) {
        parent->children[j + 1] = parent->children[j];
    }
    parent->children[index + 1] = newNode;

    for (int j = parent->numKeys - 1; j >= index; j--) {
        parent->keys[j + 1] = parent->keys[j];
    }
    parent->keys[index] = child->keys[MAX_KEYS / 2 - 1];
    parent->numKeys++;
}

// 插入新值
void insertNonFull(BTreeNode *node, int value) {
    int i = node->numKeys - 1;

    // 如果是叶子节点
    if (node->isLeaf) {
        // 找到新值插入的位置
        while (i >= 0 && value < node->keys[i]) {
            node->keys[i + 1] = node->keys[i];
            i--;
        }
        node->keys[i + 1] = value;
        node->numKeys++;
    } else {
        // 找到合适的子节点
        while (i >= 0 && value < node->keys[i]) {
            i--;
        }
        i++;
        // 如果子节点已满
        if (node->children[i]->numKeys == MAX_KEYS - 1) {
            splitChild(node, i, node->children[i]);
            // 根据新插入的值决定选择哪个子节点
            if (value > node->keys[i]) {
                i++;
            }
        }
        insertNonFull(node->children[i], value);
    }
}

// 插入操作
void insert(BTreeNode **root, int value) {
    BTreeNode *r = *root;
    // 如果根节点已满,需要分裂
    if (r->numKeys == MAX_KEYS - 1) {
        BTreeNode *newRoot = (BTreeNode *)malloc(sizeof(BTreeNode));
        newRoot->isLeaf = 0;
        newRoot->numKeys = 0;
        newRoot->children[0] = r;
        splitChild(newRoot, 0, r);
        insertNonFull(newRoot, value);
        *root = newRoot;
    } else {
        insertNonFull(r, value);
    }
}

插入操作首先检查根节点是否已满,如果已满,则进行分裂操作并将根节点更新为新的根节点。然后,递归地在子节点中插入新值。

删除操作

删除操作在B树中删除特定值,并确保节点满足B树的平衡特性。

// 找到最大值节点
BTreeNode* maxKeyNode(BTreeNode *node) {
    while (!node->isLeaf) {
        node = node->children[node->numKeys];
    }
    return node;
}

// 删除操作
void delete(BTreeNode *node, int value) {
    int i = 0;
    while (i < node->numKeys && value > node->keys[i]) {
        i++;
    }

    if (i < node->numKeys && value == node->keys[i]) {
        // 如果找到要删除的键
        if (node->isLeaf) {
            // 如果是叶子节点,直接删除
            for (int j = i; j < node->numKeys - 1; j++) {
                node->keys[j] = node->keys[j + 1];
            }
            node->numKeys--;
        } else {
            // 非叶子节点
            BTreeNode *leftChild = node->children[i];
            BTreeNode *rightChild = node->children[i + 1];

            if (leftChild->numKeys >= (MAX_KEYS + 1) / 2) {
                // 左子节点有足够的键
                BTreeNode *predecessor = maxKeyNode(leftChild);
                node->keys[i] = predecessor->keys[predecessor->numKeys - 1];
                delete(leftChild, predecessor->keys[predecessor->numKeys - 1]);
            } else if (rightChild->numKeys >= (MAX_KEYS + 1) / 2) {
                // 右子节点有足够的键
                BTreeNode *successor = minKeyNode(rightChild);
                node->keys[i] = successor->keys[0];
                delete(rightChild, successor->keys[0]);
            } else {
                // 合并左右子节点
                mergeChildren(node, i);
                delete(node->children[i], value);
            }
        }
    } else {
        // 如果要删除的键在子节点中
        if (node->isLeaf) {
            return;
        }

        int flag = (i == node->numKeys);
        if (node->children[i]->numKeys < (MAX_KEYS + 1) / 2) {
            fill(node, i);
        }

        if (flag && i > node->numKeys) {
            delete(node->children[i - 1], value);
        } else {
            delete(node->children[i], value);
        }
    }
}

删除操作通过递归的方式删除指定的值。如果要删除的键在叶子节点中,直接删除该键;否则,在子节点中查找并删除。如果子节点的键数不足,需要进行调整操作以保持树的平衡。

总结

B树是一种自平衡的多路搜索树,它适用于需要快速查找、插入和删除操作的大数据结构。B树在数据库索引和文件系统中非常常用,因为它具有较小的深度,可以有效地减少磁盘I/O操作,提高数据操作的效率。

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

游向大厂的咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值