C语言实现简单的M阶B树的小示例

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

#define M 4  // M阶B树的度为4,也就是每个节点最多可以有4棵子树

// M阶B树的节点结构体
struct node {
    int keys[M - 1];          // 关键字数组
    struct node* children[M]; // 子节点指针数组
    int nKeys;                // 关键字的数量
    bool leaf;                // 是否为叶子节点
};

// 创建新的M阶B树节点
struct node* createNode(bool isLeaf) {
    struct node* newNode = (struct node*) malloc(sizeof(struct node));
    newNode->nKeys = 0;
    newNode->leaf = isLeaf;
    for (int i = 0; i < M; i++) {
        newNode->children[i] = NULL;
    }
    return newNode;
}

// 在当前节点上查找给定的键值,并返回该键值在节点中的索引
int findKeyIndex(struct node* currentNode, int key) {
    int index = 0;
    while (index < currentNode->nKeys && currentNode->keys[index] < key) {
        index++;
    }
    return index;
}


// 分裂指定的子节点
void splitChild(struct node* currentNode, int childIndex, struct node* childNode) {
    struct node* newChildNode = createNode(childNode->leaf);
    newChildNode->nKeys = M / 2 - 1;
    for (int i = 0; i < M / 2 - 1; i++) {
        newChildNode->keys[i] = childNode->keys[i + M / 2];
    }
    if (!childNode->leaf) {
        for (int i = 0; i < M / 2; i++) {
            newChildNode->children[i] = childNode->children[i + M / 2];
        }
    }
    childNode->nKeys = M / 2 - 1;
    for (int i = currentNode->nKeys; i >= childIndex + 1; i--) {
        currentNode->children[i + 1] = currentNode->children[i];
    }
    currentNode->children[childIndex + 1] = newChildNode;
    for (int i = currentNode->nKeys - 1; i >= childIndex; i--) {
        currentNode->keys[i + 1] = currentNode->keys[i];
    }
    currentNode->keys[childIndex] = childNode->keys[M / 2 - 1];
    currentNode->nKeys++;
}


// 在当前节点上插入一个新的键值对
void insertNonFull(struct node* currentNode, int key) {
    int index = currentNode->nKeys - 1;
    if (currentNode->leaf) {
        // 如果节点是叶子节点,则找到适当的插入位置
        while (index >= 0 && currentNode->keys[index] > key) {
            currentNode->keys[index + 1] = currentNode->keys[index];
            index--;
        }
        currentNode->keys[index + 1] = key;
        currentNode->nKeys++;
    } else {
        // 如果节点是内部节点,则找到该键应该插入的子节点,并插入到子节点中
        while (index >= 0 && currentNode->keys[index] > key) {
            index--;
        }
        if (currentNode->children[index + 1]->nKeys == M - 1) {
            // 如果该子节点已经满了,则需要分裂该子节点
            splitChild(currentNode, index + 1, currentNode->children[index + 1]);
            if (currentNode->keys[index + 1] < key) {
                index++;
            }
        }
        insertNonFull(currentNode->children[index + 1], key);
    }
}
// 获取指定节点上的前驱节点
int getPredecessor(struct node* currentNode, int index) {
    struct node* currentNodeTmp = currentNode->children[index];
    while (!currentNodeTmp->leaf) {
        currentNodeTmp = currentNodeTmp->children[currentNodeTmp->nKeys];
    }
    return currentNodeTmp->keys[currentNodeTmp->nKeys - 1];
}

// 获取指定节点上的后继节点
int getSuccessor(struct node* currentNode, int index) {
    struct node* currentNodeTmp = currentNode->children[index + 1];
    while (!currentNodeTmp->leaf) {
        currentNodeTmp = currentNodeTmp->children[0];
    }
    return currentNodeTmp->keys[0];
}

// 和其右兄弟合并
void mergeNodes(struct node* currentNode, int index) {
    struct node* childNode = currentNode->children[index];
    struct node* rightSiblingNode = currentNode->children[index + 1];
    childNode->keys[M / 2 - 1] = currentNode->keys[index];
    for (int i = 0; i < rightSiblingNode->nKeys; i++) {
        childNode->keys[i + M / 2] = rightSiblingNode->keys[i];
    }
    if (!childNode->leaf) {
        for (int i = 0; i <= rightSiblingNode->nKeys; i++) {
             childNode->children[i + M / 2] = rightSiblingNode->children[i];
        }
    }
    for (int i = index + 1; i < currentNode->nKeys; i++) {
        currentNode->keys[i - 1] = currentNode->keys[i];
    }
    for (int i = index + 2; i <= currentNode->nKeys; i++) {
        currentNode->children[i - 1] = currentNode->children[i];
    }
    childNode->nKeys += rightSiblingNode->nKeys + 1;
    currentNode->nKeys--;
    free(rightSiblingNode);
}
// 从左兄弟节点中向指定子节点移动一个键
void borrowFromLeft(struct node* currentNode, int childIndex) {
    struct node* childNode = currentNode->children[childIndex];
    struct node* leftSiblingNode = currentNode->children[childIndex - 1];
    for (int i = childNode->nKeys - 1; i >= 0; i--) {
        childNode->keys[i + 1] = childNode->keys[i];
    }
    if (!childNode->leaf) {
        for (int i = childNode->nKeys; i >= 0; i--) {
            childNode->children[i + 1] = childNode->children[i];
        }
    }
    childNode->keys[0] = currentNode->keys[childIndex - 1];
    if (!leftSiblingNode->leaf) {
        childNode->children[0] = leftSiblingNode->children[leftSiblingNode->nKeys];
    }
    currentNode->keys[childIndex - 1] = leftSiblingNode->keys[leftSiblingNode->nKeys - 1];
    childNode->nKeys++;
    leftSiblingNode->nKeys--;
}

// 从右兄弟节点中向指定子节点移动一个键
void borrowFromRight(struct node* currentNode, int childIndex) {
    struct node* childNode = currentNode->children[childIndex];
    struct node* rightSiblingNode = currentNode->children[childIndex + 1];
    childNode->keys[childNode->nKeys] = currentNode->keys[childIndex];
    if (!childNode->leaf) {
        childNode->children[childNode->nKeys + 1] = rightSiblingNode->children[0];
    }
    currentNode->keys[childIndex] = rightSiblingNode->keys[0];
    for (int i = 1; i < rightSiblingNode->nKeys; i++) {
        rightSiblingNode->keys[i - 1] = rightSiblingNode->keys[i];
    }
    if (!rightSiblingNode->leaf) {
        for (int i = 1; i <= rightSiblingNode->nKeys; i++) {
            rightSiblingNode->children[i - 1] = rightSiblingNode->children[i];
        }
    }
    rightSiblingNode->nKeys--;
    childNode->nKeys++;
}

// 在指定节点的子节点中移动一个键,以确保最少的键
void fillChild(struct node* currentNode, int childIndex) {
    if (childIndex != 0 && currentNode->children[childIndex - 1]->nKeys >= M / 2) {
        // 如果子节点的左兄弟节点有M/2个或更多键,则从左兄弟节点中向该子节点移动一个键
        borrowFromLeft(currentNode, childIndex);
    } else if (childIndex != currentNode->nKeys && currentNode->children[childIndex + 1]->nKeys >= M / 2) {
        // 如果子节点的右兄弟节点具有M/2个或更多键,则从右兄弟节点中向该子节点移动一个键
        borrowFromRight(currentNode, childIndex);
    } else {
        // 如果该子节点和其所有相邻兄弟节点都只有M/2-1个键,则将该子节点与其中一个相邻兄弟节点合并
        if (childIndex != currentNode->nKeys) {
            mergeNodes(currentNode, childIndex);
        } else {
            mergeNodes(currentNode, childIndex - 1);
        }
    }
}

// 删除节点上的某个键值对
void deleteKey(struct node* currentNode, int key) {
    int index = findKeyIndex(currentNode, key);
    if (index < currentNode->nKeys && currentNode->keys[index] == key) {
        if (currentNode->leaf) {
            // 如果节点是叶子节点并且包括该键,则删除该键
            for (int i = index; i < currentNode->nKeys - 1; i++) {
                currentNode->keys[i] = currentNode->keys[i + 1];
            }
            currentNode->nKeys--;
        } else {
            // 如果节点是内部节点,则在子节点中找到与该键紧随其后的键,并用该键替换该键
            if (currentNode->children[index]->nKeys >= M / 2) {
                int predecessorKey = getPredecessor(currentNode, index);
                currentNode->keys[index] = predecessorKey;
                deleteKey(currentNode->children[index], predecessorKey);
            } else if (currentNode->children[index + 1]->nKeys >= M / 2) {
                int successorKey = getSuccessor(currentNode, index);
                currentNode->keys[index] = successorKey;
                deleteKey(currentNode->children[index + 1], successorKey);
            } else {
                // 如果子节点和其右兄弟节点都只有M/2-1个键,则将两个节点合并
                mergeNodes(currentNode, index);
                deleteKey(currentNode->children[index], key);
            }
        }
    } else {
        // 如果节点不是叶子节点,则在适当的子树上递归删除该键
        if (currentNode->leaf) {
            printf("指定键值 %d 不存在于树中。\n", key);
            return;
        }
        bool flag = (index == currentNode->nKeys);
        if (currentNode->children[index]->nKeys < M / 2) {
            // 如果所选择的子节点只有M/2-1个键,则需要对该子节点进行向左移动或合并
            fillChild(currentNode, index);
        }
        if (flag && index > currentNode->nKeys) {
            deleteKey(currentNode->children[index - 1], key);
        } else {
            deleteKey(currentNode->children[index], key);
        }
    }
}


//在m阶B树中查找一个键值,如果找到返回true,否则返回false
bool search(struct node* currentNode, int key) {
    int index = findKeyIndex(currentNode, key);
    if (index < currentNode->nKeys && currentNode->keys[index] == key) {
        return true;
    }
    return (!currentNode->leaf && search(currentNode->children[index], key));
}

// 打印B树中的元素
void traverse(struct node* currentNode) {
    int i;
    for (i = 0; i < currentNode->nKeys; i++) {
        if (!currentNode->leaf) {
            traverse(currentNode->children[i]);
        }
        printf(" %d", currentNode->keys[i]);
    }
    if (!currentNode->leaf) {
        traverse(currentNode->children[i]);
    }
}

// 主函数
int main() {
    struct node* root = NULL;
    root = createNode(true);
    int choice, key;
    while (1) {
        printf("\n-----------------------------------------------");
        printf("\nM阶B树\n");
        printf("1.插入元素\n2.删除元素\n3.查找元素\n4.打印元素\n5.退出\n");
        printf("请选择您要进行的操作:");
        scanf("%d", &choice);
        switch (choice) {
            case 1:
                printf("请输入要插入的元素:");
                scanf("%d", &key);
                if (search(root, key)) {
                    printf("该元素已存在于树中。\n");
                    break;
                }
                if (root->nKeys == M - 1) {
                    // 如果根节点已满,则需要创建新的根节点
                    struct node* newRootNode = createNode(false);
                    newRootNode->children[0] = root;
                    splitChild(newRootNode, 0, root);
                    insertNonFull(newRootNode, key);
                    root = newRootNode;
                } else {
                    insertNonFull(root, key);
                }
                printf("元素 %d 已插入树中。\n", key);
                break;
            case 2:
                printf("请输入要删除的元素:");
                scanf("%d", &key);
                if (!search(root, key)) {
                    printf("该元素不存在于树中。\n");
                    break;
                }
                deleteKey(root, key);
                printf("元素 %d 已从树中删除。\n", key);
                break;
            case 3:
                printf("请输入要查找的元素:");
                scanf("%d", &key);
                if (search(root, key)) {
                    printf("该元素存在于树中。\n");
                } else {
                    printf("该元素不存在于树中。\n");
                }
                break;
            case 4:
                printf("树中的元素是:");
                traverse(root);
                printf("\n");
                break;
            case 5:
                exit(0);
            default:
                printf("请输入正确的操作编号。\n");
        }
    }
    return 0;
}
 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值