📜  B-Tree中的删除操作(1)

📅  最后修改于: 2023-12-03 15:29:33.839000             🧑  作者: Mango

B-Tree中的删除操作

B-Tree是一种自平衡的搜索树,它的节点可以有多个子节点和关键字,通常被用于在磁盘或其他外部存储设备上存储和索引数据。 B-Tree的特点是具有高度平衡的层数和大节点内部存储的关键字数,使得 B-Tree 可以在外部设备上进行高效的查询、插入和删除操作。

在B-Tree中,删除节点的操作比插入节点的操作稍微复杂一些。下面将介绍B-Tree中的删除操作,并通过代码片段演示其实现过程。

删除节点的一般步骤

B-Tree中删除节点的一般步骤如下:

  1. 首先,在B-Tree中搜索需要删除的节点。如果找不到该节点,则删除操作不进行任何操作,返回B-Tree原样。

  2. 如果找到了该节点,那么我们需要考虑该节点的几种情况:

    • 叶子节点:如果需要删除的节点是一个叶子节点,那么可以直接删除该节点,并且要将其从父节点中删除。

    • 内部节点: 如果该节点为内部节点,则需要找到该节点的前驱或后继节点来替换掉该节点的位置,并将其删除。对于内部节点,在删除该节点后要对B-Tree进行平衡操作,以保证B-Tree的性质不被破坏。

删除叶子节点的代码实现
/**
 * 从B树中删除key
 * 
 * @param key 要删除的key
 * @return true:删除成功,false:删除失败
 */
bool delete(BTree &bTree, KeyType key)
{
    // 如果B-Tree为空,则直接返回false
    if (bTree == NULL) {
        return false;
    }
    
    // 找到该节点的位置
    int i = search_key(bTree, key);
    
    // 如果找到的位置不匹配,则返回false
    if (i < bTree->keyNum && bTree->key[i] == key) {
        // 如果找到的是一个叶子节点,则直接删除该节点
        if (bTree->is_leaf) {
            delete_key_from_leaf_node(bTree, i);
        } else {
            // 如果该节点不是叶子节点,则递归继续删除其后继节点
            BTree p = get_right_prev_child(bTree, i);
            p = get_max_key(p);
            bTree->key[i] = p->key[p->keyNum-1];
            delete(p, bTree->key[i]);
        }
        
        // 对B-Tree进行平衡操作
        if (bTree->keyNum < bTreeOrder / 2) {
            balance_tree(bTree);
        }
        return true;
    } else {
        // 如果没找到,则继续递归查找
        if (bTree->is_leaf) {
            // 如果没有找到,且当前树为叶子节点,则说明Key不存在
            return false;
        } else {
            delete(bTree->child[i], key);
        }
    }
}
删除内部节点的代码实现
/**
 * 在B-Tree的节点p中删除索引为i的关键字
 */
void delete_key(BTree &p, int i)
{
    // 判断是否是叶子节点
    if (p->is_leaf) {
        // 如果是叶子节点,则删除该关键字
        delete_key_from_leaf_node(p, i);
        return;
    }
    
    // 如果当前节点中的子节点的关键字数量>=t,则可以直接从该子节点中获取该节点的前驱或后继节点,并进行替换
    if (p->child[i]->keyNum >= bTreeOrder / 2) {
        BTree q = p->child[i];
        p->key[i] = get_prev_key(q, q->keyNum - 1);
        delete(q, p->key[i]);
    }
    // 如果当前节点种的子节点的数量<t,则需要从当前节点或者其兄弟节点中借关键字
    else {
        // 如果第i个节点的左右兄弟节点都有至少t个关键字,则从右兄弟节点中获取一个关键字并替换,然后继续删除右子树中的节点
        if ((i > 0) && (p->child[i - 1]->keyNum >= bTreeOrder / 2)) {
            BTree q = p->child[i];
            BTree borrowFromLeft = p->child[i - 1];
            for (int i = q->keyNum; i > 0; i--) {
                q->key[i] = q->key[i - 1];
                q->child[i + 1] = q->child[i];
            }
            q->key[0] = p->key[i - 1];
            q->child[1] = q->child[0];
            q->child[0] = borrowFromLeft->child[borrowFromLeft->keyNum];
            q->keyNum++;
            p->key[i - 1] = borrowFromLeft->key[borrowFromLeft->keyNum - 1];
            borrowFromLeft->keyNum--;
            delete(q, key);
        } 
        // 如果第i个节点的左兄弟节点不足t个关键字且右兄弟节点有至少t个,那么从右兄弟节点中借一个关键字并进行替换,并继续删除
        else if ((i < p->keyNum) && (p->child[i + 1]->keyNum >= bTreeOrder / 2)) {
            BTree q = p->child[i];
            BTree borrowFromRight = p->child[i + 1];
            q->keyNum++;
            q->key[q->keyNum - 1] = p->key[i];
            q->child[q->keyNum] = borrowFromRight->child[0];
            p->key[i] = borrowFromRight->key[0];
            borrowFromRight->keyNum--;
            for (int i = 0; i < borrowFromRight->keyNum; i++) {
                borrowFromRight->key[i] = borrowFromRight->key[i + 1];
                borrowFromRight->child[i] = borrowFromRight->child[i + 1];
            }
            borrowFromRight->child[borrowFromRight->keyNum] = borrowFromRight->child[borrowFromRight->keyNum + 1];
            delete(q, key);
        }
        // 如果当前节点和其兄弟节点都小于t个关键字,则需要将当前节点与兄弟兄弟节点合并,将原来的p[i]和兄弟节点和当前节点合并,使得合并后的节点数量达到2t-1
        else {
            if ((i > 0) && (p->child[i - 1]->keyNum == bTreeOrder / 2 - 1)) {
                BTree left = p->child[i - 1];
                BTree right = p->child[i];
                
                left->keyNum++;
                left->key[left->keyNum - 1] = p->key[i - 1];
                left->child[left->keyNum] = right->child[0];
                
                for (int j = 0; j < right->keyNum; j++) {
                    left->keyNum++;
                    left->key[left->keyNum - 1] = right->key[j];
                    left->child[left->keyNum] = right->child[j + 1];
                }
                
                for (int j = i - 1; j < p->keyNum - 1; j++) {
                    p->key[j] = p->key[j + 1];
                    p->child[j + 1] = p->child[j + 2];
                }
                p->keyNum--;
                
                delete(left, key);
            } else {
                BTree left = p->child[i];
                BTree right = p->child[i + 1];
                
                left->keyNum++;
                left->key[left->keyNum - 1] = p->key[i];
                left->child[left->keyNum] = right->child[0];
                
                for (int j = 0; j < right->keyNum; j++) {
                    left->keyNum++;
                    left->key[left->keyNum - 1] = right->key[j];
                    left->child[left->keyNum] = right->child[j + 1];
                }
                
                for (int j = i; j < p->keyNum - 1; j++) {
                    p->key[j] = p->key[j + 1];
                    p->child[j + 1] = p->child[j + 2];
                }
                p->keyNum--;
                
                delete(left, key);
            }
        }
    }
}