B树的简单实现

时间:2024-11-21 12:29:59
template<class K, size_t M>
struct BTreeNode
{
    K _keys[M]; // 用于存储关键字的数组,最多容纳 M 个关键字(超额一个,为分裂提供空间)。
    BTreeNode<K, M>* _subs[M + 1]; // 存储子节点的指针数组,最多 M+1 个子节点。
    BTreeNode<K, M>* _parent; // 指向父节点的指针。
    size_t _n; // 当前节点中实际存储的关键字数量。

    // 构造函数,初始化节点的各个属性。
    BTreeNode()
    {
        for (size_t i = 0; i < M; i++)
        {
            _keys[i] = K(); // 初始化关键字数组为默认值。
            _subs[i] = nullptr; // 初始化子节点指针为空。
        }
        _subs[M] = nullptr; // 多余的子节点指针初始化为空。
        _parent = nullptr; // 父节点初始化为空。
        _n = 0; // 初始关键字数量为 0。
    }
};
  • 作用:定义 B 树节点的结构,包括关键字数组、子节点指针、父节点指针和当前节点的关键字数量。
  • 意义
    • 为 B 树的基本操作(插入、删除、查找等)提供节点存储结构。
    • 允许节点存储最多 M 个关键字和 M+1 个子节点。

template<class K, size_t M>
class BTree
{
    typedef BTreeNode<K, M> Node; // 定义一个别名,便于后续引用节点类型。

public:

 pair<Node*, int> Find(const K& key)
{
    Node* cur = _root; // 从根节点开始查找。
    Node* parent = nullptr; // 记录当前节点的父节点。
    while (cur)
    {
        size_t i = 0;
        // 在当前节点中逐一比较关键字
        while (i < cur->_n)
        {
            if (key < cur->_keys[i]) // 如果目标关键字小于当前关键字,停止查找。
            {
                break;
            }
            else if (key > cur->_keys[i]) // 如果目标关键字大于当前关键字,继续查找。
            {
                i++;
            }
            else // 找到目标关键字,返回节点和索引。
            {
                return make_pair(cur, i);
            }
        }

        parent = cur; // 更新父节点为当前节点。
        cur = cur->_subs[i]; // 进入对应的子节点。
    }

    return make_pair(parent, -1); // 未找到,返回最近的父节点及无效索引。
}





void InsertKey(Node* node, const K& key, Node* child)
{
    int end = node->_n - 1; // 从节点的最后一个关键字开始比较。
    while (end >= 0)
    {
        if (key < node->_keys[end]) // 如果新关键字小于当前关键字,向右移动当前关键字和其右子树。
        {
            node->_keys[end + 1] = node->_keys[end];
            node->_subs[end + 2] = node->_subs[end + 1];
            end--;
        }
        else // 如果新关键字大于等于当前关键字,停止移动。
        {
            break;
        }
    }
    node->_keys[end + 1] = key; // 插入新关键字。
    node->_subs[end + 2] = child; // 插入对应的子节点。
    if (child)
    {
        child->_parent = node; // 更新子节点的父节点指针。
    }
    node->_n++; // 更新节点的关键字数量。
}





bool Insert(const K& key)
{
    if (_root == nullptr) // 如果树为空,创建根节点并插入关键字。
    {
        _root = new Node;
        _root->_keys[0] = key;
        _root->_n++;

        return true;
    }
    // key已经存在,不允许插入
    pair<Node*, int> ret = Find(key);
    if (ret.second >= 0)
    {
        return false;
    }
    K newKey = key; // 保存当前要插入的关键字。
    Node* child = nullptr; // 保存分裂后产生的新节点。
    Node* parent = ret.first; // 从查找到的父节点开始插入。

    while (1)
    {
        InsertKey(parent, newKey, child); // 插入关键字到节点。
        if (parent->_n < M) // 如果节点未满,插入完成。
        {
            return true;
        }
        else // 如果节点满了,进行分裂。
        {
            size_t mid = M / 2; // 找到中间关键字的位置。
            Node* brother = new Node; // 创建一个新节点作为兄弟节点。

            size_t j = 0;
            size_t i = mid + 1;
            for (; i <= M - 1; i++) // 将中间关键字右边的部分移动到兄弟节点。
            {
                brother->_keys[j] = parent->_keys[i];
                brother->_subs[j] = parent->_subs[i];

                if (parent->_subs[i])
                {
                    parent->_subs[i]->_parent = brother;
                }
                j++;
            }

            brother->_subs[j] = parent->_subs[i]; // 拷贝最后一个右子树。
            if (parent->_subs[i])
            {
                parent->_subs[i]->_parent = brother;
            }

            brother->_n = j; // 更新兄弟节点的关键字数量。
            parent->_n -= (brother->_n + 1); // 更新父节点的关键字数量。

            K midkey = parent->_keys[mid]; // 提取中间关键字。
            parent->_keys[mid] = K(); // 清空中间关键字。

            if (parent->_parent == nullptr) // 如果分裂的是根节点,创建新根节点。
            {
                _root = new Node;
                _root->_keys[0] = midkey;
                _root->_subs[0] = parent;
                _root->_subs[1] = brother;
                _root->_n = 1;

                parent->_parent = _root;
                brother->_parent = _root;
                break;
            }
            else // 如果分裂的不是根节点,继续向上插入。
            {
                newKey = midkey;
                child = brother;
                parent = parent->_parent;
            }
        }
    }
    return true;
}





void _Inorder(Node* root)
{
    if (root == nullptr)
    {
        return;
    }

    size_t i = 0;
    for (i = 0; i < root->_n; i++) // 遍历当前节点的关键字和子树。
    {
        _Inorder(root->_subs[i]); // 递归遍历左子树。
        cout << root->_keys[i] << " "; // 打印当前关键字。
    }
    _Inorder(root->_subs[i]); // 遍历最后一个右子树。
}




 

void TestBtree()
{
    int a[] = {53, 139, 75, 49, 145, 36, 101}; // 要插入的关键字数组。
    BTree<int, 3> t; // 创建阶数为 3 的 B 树。

    for (auto e : a)
    {
        t.Insert(e); // 插入每个关键字。
    }
    t.Inorder(); // 打印中序遍历结果。
}