首页 > 其他分享 >【数据结构】B树

【数据结构】B树

时间:2024-07-12 13:55:21浏览次数:19  
标签:node keys int child 数据结构 root 节点

B树介绍

B树(B-Tree)是一种自平衡的树结构,它维持数据的有序性,并且允许搜索、顺序访问、插入和删除操作都在对数时间内完成。B树是为磁盘和其他直接访问的辅助存储设备而设计的,主要用于数据库和文件系统中。以下是B树的一些主要特点:

B树的特性:

  1. 节点最大和最小孩子数:一个B树的节点可能有许多孩子,从几个到数千个。每个节点除了叶子节点外,都有至少两个孩子。
  2. 节点中的键(Key):每个节点包含一定数量的键。这些键按照升序排列,并分隔开节点指针,使得每个键都是一个子树的分隔键。
  3. 分支因子:B树的每个节点最多包含的子节点数称为分支因子,通常用字母m表示。一个节点的键的数量比它孩子的数量少1,即一个节点包含最多m-1个键。
  4. 平衡性:B树是平衡的,所有叶子节点都在同一层。
  5. 填充因子:为了保持树的平衡,B树在插入和删除操作时会进行节点的分裂和合并。节点的分裂和合并会确保树的高度尽可能低。

B树的操作:

  • 搜索:从根节点开始,根据要查找的键与节点的键进行比较,决定是继续在左子树还是右子树中搜索。
  • 插入:在找到合适的叶子节点后,将新键插入该节点。如果节点中的键的数量超过了m-1,则节点会分裂成两个节点,并将中间的键提升到父节点。
  • 删除:找到包含要删除键的节点,并从节点中移除该键。如果删除后节点中的键的数量少于要求的最小数量,则需要重新分配或合并节点。

B树的应用:

  • 数据库索引:B树由于其高效的磁盘I/O操作,常被用作数据库系统的索引结构。
  • 文件系统:许多文件系统使用B树或其变种(如B+树)来存储文件系统的元数据。
    B树之所以高效,是因为它减少了磁盘I/O操作的次数。在数据库和文件系统中,磁盘I/O是非常耗时的操作,因此减少磁盘访问次数可以显著提高性能。
    以下是一个简单的B树示例,我们将通过一个具体的例子来展示B树的搜索、插入、和删除操作。假设我们有一个B树,其分支因子为4,这意味着每个节点最多可以有3个键和4个子节点。

案例

结构理解

初始状态

首先,我们从一个空的B树开始,然后逐步执行插入操作。

空B树

插入操作

现在,我们将以下键按顺序插入到B树中:10, 20, 5, 6, 12, 30, 7, 17

  1. 插入10
     10
  1. 插入20
     10     20
  1. 插入5
     5      10     20
  1. 插入6
     5      6      10     20
  1. 插入12
     5      6      10     12     20
  1. 插入30
    因为节点已满,我们需要分裂节点:
     6
   /   \
  5     10
       /   \
      12    20
             \
              30
  1. 插入7
     6
   /   \
  5     10
       /   \
      7    12
             \
              20
             /
            30
  1. 插入17
    因为节点已满,我们需要再次分裂节点:
       10
     /    \
    6      20
   / \    /  \
  5   7  12   30
           \
            17

搜索操作

假设我们要搜索键值为17的节点。

  1. 从根节点10开始,因为17 > 10,所以我们搜索右子树。
  2. 在节点20的左子树中,因为17 < 20,我们搜索节点12。
  3. 在节点12的右子树中,我们找到了键17。

删除操作

现在,假设我们要删除键值为7的节点。

  1. 找到键7所在的节点,它是节点6的左孩子。
  2. 删除键7。
  3. 因为删除后节点6仍然有至少2个键,所以不需要进行节点合并或重新分配。
    删除后的B树如下:
       10
     /    \
    6      20
   / \    /  \
  5   7  12   30
           \
            17

需要注意的是删除操作可能会导致节点的合并或重新分配,但在本例中,我们假设删除操作不会触发这些情况。如果删除操作导致节点键的数量少于最小要求,那么需要从兄弟节点借键或与兄弟节点合并,以保持B树的平衡。

C代码实现

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

#define MAX_KEYS 3
#define MIN_KEYS 2

typedef struct BTreeNode {
    int keys[MAX_KEYS + 1];
    struct BTreeNode *child[MAX_KEYS + 2];
    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 <= MAX_KEYS; i++) {
        node->keys[i] = 0;
        node->child[i] = NULL;
    }
    node->child[MAX_KEYS + 1] = NULL;
    return node;
}

void insert(BTreeNode **root, int key) {
    if (*root == NULL) {
        *root = create_node(1);
        (*root)->keys[0] = key;
        (*root)->num_keys = 1;
        return;
    }

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

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

void split_child(BTreeNode *node, int i) {
    BTreeNode *z = create_node(node->child[i]->is_leaf);
    z->num_keys = MIN_KEYS;
    for (int j = 0; j < MIN_KEYS; j++) {
        z->keys[j] = node->child[i]->keys[j + MIN_KEYS + 1];
    }
    if (!node->child[i]->is_leaf) {
        for (int j = 0; j < MIN_KEYS + 1; j++) {
            z->child[j] = node->child[i]->child[j + MIN_KEYS + 1];
        }
    }
    node->child[i]->num_keys = MIN_KEYS;
    for (int j = node->num_keys; j >= i + 1; j--) {
        node->child[j + 1] = node->child[j];
    }
    node->child[i + 1] = z;
    for (int j = node->num_keys - 1; j >= i; j--) {
        node->keys[j + 1] = node->keys[j];
    }
    node->keys[i] = node->child[i]->keys[MIN_KEYS];
    node->num_keys++;
}

BTreeNode *search(BTreeNode *root, int key) {
    int i = 0;
    while (i < root->num_keys && key > root->keys[i]) {
        i++;
    }
    if (i < root->num_keys && key == root->keys[i]) {
        return root;
    }
    if (root->is_leaf) {
        return NULL;
    } else {
        return search(root->child[i], key);
    }
}

void delete(BTreeNode **root, int key) {
    if (*root == NULL) return;

    int i = 0;
    while (i < (*root)->num_keys && key > (*root)->keys[i]) {
        i++;
    }

    if (i < (*root)->num_keys && key == (*root)->keys[i]) {
        if ((*root)->is_leaf) {
            for (int j = i; j < (*root)->num_keys - 1; j++) {
                (*root)->keys[j] = (*root)->keys[j + 1];
            }
            (*root)->num_keys--;
            if ((*root)->num_keys == 0) {
                free(*root);
                *root = NULL;
            }
        } else {
        
        }
    } else {
        if ((*root)->is_leaf) {
            return; // Key not found in the tree
        }

        // The key might be in the child
        BTreeNode *child = (*root)->child[i];
        if (child->num_keys == MIN_KEYS) {
            // 如果删除操作导致节点键的数量少于最小要求,那么需要从兄弟节点借键或与兄弟节点合并,以保持B树的平衡
        } else {
            delete(&child, key);
        }
    }
}

void print_tree(BTreeNode *node, int level) {
    if (node != NULL) {
        for (int i = 0; i < node->num_keys; i++) {
            print_tree(node->child[i], level + 1);
            for (int j = 0; j < level; j++) printf("   ");
            printf("%d\n", node->keys[i]);
        }
        print_tree(node->child[node->num_keys], level + 1);
    }
}

int main() {
    BTreeNode *root = NULL;

    int keys_to_insert[] = {10, 20, 5, 6, 12, 30, 7, 17};
    int num_keys = sizeof(keys_to_insert) / sizeof(keys_to_insert[0]);

    for (int i = 0; i < num_keys; i++) {
        insert(&root, keys_to_insert[i]);
    }

    printf("B-Tree after insertions:\n");
    print_tree(root, 0);

    BTreeNode *search_result = search(root, 17);
    if (search_result != NULL) {
        printf("Key 17 found in BTree.\n");
    } else {
        printf("Key 17 not found in BTree.\n");
    }

    delete(&root, 7);
    printf("B-Tree after deleting 7:\n");
    print_tree(root, 0);

    return 0;
}

这个程序展示了如何创建一个B树,插入一系列键,搜索一个键,并简单地删除一个键。删除操作只处理了叶子节点的情况,并没有处理节点合并或重新分配的复杂情况。打印函数print_tree用于可视化地展示B树的结构。

标签:node,keys,int,child,数据结构,root,节点
From: https://blog.csdn.net/Young_Pro/article/details/140351433

相关文章

  • 【初阶数据结构】树与二叉树:从零开始的奇幻之旅
    初阶数据结构相关知识点可以通过点击以下链接进行学习一起加油!时间与空间复杂度的深度剖析深入解析顺序表:探索底层逻辑深入解析单链表:探索底层逻辑深入解析带头双向循环链表:探索底层逻辑深入解析栈:探索底层逻辑深入解析队列:探索底层逻辑深入解析循环队列:探索底层逻辑......
  • 山东大学数据结构与算法课程设计实验4网络放大器设置问题
    问题描述 一个汽油传送网络可由加权有向无环图G表示。图中有一个称为源点的顶点S。从S出发,汽油被输送到图中的其他顶点。S的入度为0,每一条边上的权给出了它所连接的两点间的距离。通过网络输送汽油时,压力的损失是所走距离的函数。为了保证网络的正常运转,在网络传输中必须保......
  • Redis数据结构—跳跃表 skiplist 实现源码分析
    Redis是一个开源的内存数据结构存储系统,它可以用作数据库、缓存和消息中间件。Redis的数据结构非常丰富,其中跳跃表(skiplist)是一种重要的数据结构,它被用来实现有序集合(sortedsets)。跳跃表是一种概率型数据结构,它通过多层链表来实现快速的查找操作。跳跃表的结构类似于多层索引,......
  • 数据结构笔记之表达式求值
    概念:运算是由运符和操作数组成的,DIY概念指的是左操作数、右操作数和运符之间的关系。中缀表达式:运符位于操作数之间,这是我们日常生活中最常用的表达式形式。后缀表达式:运符在操作数后面,这种表达式形式没有括号,易于解析。前缀表达式:运符在操作数前面,同样没有括号,但需要遵循不同......
  • Java 算法和数据结构 答案整理,最新面试题
    Java中如何使用动态规划求解背包问题?1、定义子问题:首先确定动态规划状态,通常以物品数量和背包容量为变量定义子问题,例如dp[i][j]表示前i件物品放入容量为j的背包所能获得的最大价值。2、确定状态转移方程:基于是否选择当前物品,将问题分为两个子问题,即dp[i][j]=......
  • 【C++】通讯录管理系统+少量数据结构
    #include<iostream>#include<string>usingnamespacestd;#definemax1000structnewp{ stringname; intsex; intage; stringnumber; stringadd;};structbooks{ structnewpa[max]; intsize;};staticvoidshowMenu(){ cou......
  • 数据结构复习计划之复杂度分析(时间、空间)
    第二节:算法时间复杂度和空间复杂度算法(Algorithm):是对特定问题求解方法(步骤)的一种描述,是指令的有限序列,其中每一条指令表示一个或多个操作。算法可以有三种表示形式: 伪代码 自然语言 流程图算法的五个特性①有穷性:一个算法必须总是在执行有穷步......
  • 【数据结构】双向链表
    Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎~~......
  • 数据结构(复杂度)
    复杂度算法在编写成可执行程序后,运⾏时需要耗费时间资源和空间(内存)资源。因此衡量⼀个算法的好坏,⼀般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。时间复杂度主要衡量⼀个算法的运⾏快慢,⽽空间复杂度主要衡量⼀个算法运⾏所需要的额外空间。在计算机发展的......
  • 嵌入式学习——C语言数据结构(三)
    七、赋值运算符    1、+=     加且赋值         C += A;等价于C=C+A    2、-=      减且赋值         C -= A;等价于C=C-A    3、*=      乘且赋值      ......