B – 树

B – 树B – 树(或者 B 树,英语:B-Tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间 O(logn) 内完成。与其它一般化的二叉查找树不同,它可以拥有多余 2 个子结点。 B – 树为系统大块数据的读写操作做

一:背景

B – 树(或者 B 树,英语:B-Tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间 O ( l o g n )” role=”presentation”> O(logn) 内完成。与其它一般化的二叉查找树不同,它可以拥有多余 2 个子结点。

B – 树为系统大块数据的读写操作做了优化,减少定位记录时所经历的中间过程,从而加快存取速度。这种数据结构可以用来描述外部存储,因此常被应用在数据库和文件系统的实现上。

一棵 B – 树具有如下性质:

  1. 所有的叶子结点在同一层;
  2. 每棵 B – 树有一个 Minimum Degree,称其为 t(t 的值取决于磁盘块的大小);
  3. 除了根结点,其余每个结点至少包含 t-1 个 keys,根结点可以只包含 1 个 key;
  4. 每个结点(包括根结点)最多包含 2t-1 个 keys;
  5. 一个结点的孩子指针数等于这个结点的 keys 数 + 1;
  6. 每个结点的 keys 都按升序排列;
  7. 对于每个 key,其左边孩子结点的所有 keys 都小于它,右边孩子结点的所有 keys 都大于它。

如下图,是一棵 Minimum Degree 为 3(即 t=3)的 B – 树:

B - 树

二:算法过程与分析

首先大致看下程序的轮廓:

struct Node
{
    bool leaf;        // 是否是叶子结点
    int n;            // keys 的数目
    int * keys;       // 保存 keys
    Node ** siblings; // 保存孩子指针

    Node(int t, bool leaf)
    {
        this->leaf = leaf;
        this->n = 0;
        this->keys = new int[2 * t - 1];
        this->siblings = new Node*[2 * t]{ 0 };
    }
};

class BTree
{
private:
    int t; // Minimum Degree
    Node * root;
private:
    void destroy(Node * node);
    void split_child(Node * parent, int i, Node * child);
    void insert_non_full(Node * node, int key);
    bool find_real(Node * node, int key);
    void merge(Node * node, int i);
    void erase_real(Node * node, int key);
public:
    BTree(int t);
    ~BTree();
    void insert(int key);
    bool find(int key);
    void erase(int key);
    void print();
};

注意,以下讲解,默认 Minimum Degree = 3。

2.1 插入操作

我们举个例子来具体说明插入是如何完成的。

(1)

B - 树

对于一棵空树,直接插入即可。

(2)

B - 树

再插入 20,30,40 和 50,此时根结点 keys 数正好达到上限 5(由 2t – 1 = 6 – 1 = 5 得)。

(3)

B - 树

再插入 60 的话,根结点的 keys 就会超过上限,那么我们的做法就是先把它一分为二,具体做法是:把中间的那个 key 移到上面,再申请一个新的结点,把最后 t-1 个 keys 移到新结点里。参见图四的左部分;

接着再插入 60。参见图四的右部分。

(4)

B - 树

再插入 70 和 80,此时根结点最右边孩子结点的 keys 数已达到上限。

(5)

B - 树

再插入 90 的话,按照之前一分为二的思想,把中间的 key,即 60 移到上面,再申请一个新结点来存储最后 t-1 个 keys,最后插入 90。

插入操作的代码如下:

/* 一分为二 */
void BTree::split_child(Node * parent, int i, Node * child)
{
    Node * z = new Node(t, child->leaf);
    z->n = t - 1;

    for (int j = 0; j < t - 1; j++)
        z->keys[j] = child->keys[j + t];

    if (!child->leaf)
    {
        for (int j = 0; j < t; j++)
            z->siblings[j] = child->siblings[j + t];
    }

    child->n = t - 1;

    for (int j = parent->n; j >= i + 1; j--)
        parent->siblings[j + 1] = parent->siblings[j];

    parent->siblings[i + 1] = z;

    for (int j = parent->n - 1; j >= i; j--)
        parent->keys[j + 1] = parent->keys[j];

    parent->keys[i] = child->keys[t - 1];

    parent->n++;
}

void BTree::insert_non_full(Node * node, int key)
{
    int i = node->n - 1;

    if (node->leaf)
    {
        while (i >= 0 && node->keys[i] > key)
        {
            node->keys[i + 1] = node->keys[i];
            i--;
        }

        node->keys[i + 1] = key;
        node->n++;
    }
    else
    {
        while (i >= 0 && node->keys[i] > key)
            i--;

        if (node->siblings[i + 1]->n == 2 * t - 1)
        {
            split_child(node, i + 1, node->siblings[i + 1]);
            if (node->keys[i + 1] < key)
                i++;
        }

        insert_non_full(node->siblings[i + 1], key);
    }
}

void BTree::insert(int key)
{
    if (!root)
    {
        root = new Node(t, true);
        root->keys[0] = key;
        root->n = 1;
    }
    else
    {
        if (root->n == 2 * t - 1)
        {
            Node * s = new Node(t, false);
            s->siblings[0] = root;
            split_child(s, 0, root);

            int i = 0;
            if (s->keys[0] < key)
                i++;

            insert_non_full(s->siblings[i], key);
            root = s;
        }
        else
            insert_non_full(root, key);
    }
}

2.2 查找操作

查找很简单,直接看代码即可。

bool BTree::find_real(Node * node, int key)
{
    int i = 0;
    while (i < node->n && node->keys[i] < key)
        i++;

    if (node->keys[i] == key)
        return true;

    if (node->leaf)
        return false;

    return find_real(node->siblings[i], key);
}

bool BTree::find(int key)
{
    if (root)
        return find_real(root, key);

    return false;
}

2.3 删除操作

假设我们要删除的值是 key,在当前这棵 B – 树中它存在且位于结点 t 里。

  • t 是叶子结点
    • 这种情况很简单,直接删除即可。
  • t 不是叶子结点。分三种情况,我们约定 key 的左边的孩子结点为 left,右边的孩子结点为 right,
    • left 的 keys 数大于 t-1,那就在 left 中找到 key 的 “前驱”,用 “前驱” 替换 key,转而继续删除 “前驱”;
    • right 的 keys 数大于 t-1,那就在 right 中找到 key 的 “后继”,用 “后继” 替换 key,转而继续删除 “后继”;
    • left 和 right 的 keys 数都等于 t-1,那就把 left,key 和 right 合并为一个结点,继续在这个结点中删除 key。

但是上述做法存在一个问题,考虑:若 t 是叶子结点(不是根结点),且 keys 数恰好等于 t-1,那么我们直接删除 key 的话,势必会使这棵 B – 树违反 ” 性质 3:除了根结点,其余每个结点至少包含 t-1 个 keys,根结点可以只包含 1 个 key “。那如何解决这个问题呢?

删除伴随着查找,从根结点开始,向右或向下移动指针查找 key 的位置所在。我们可以提前判断要下降的那个结点(不妨设为 cur),如果 cur 的 keys 数等于 t-1,我们就给它填充,使其 keys 数大于 t-1。具体做法是:如果 cur 的左右兄弟结点中存在 keys 数大于 t-1 的,就从那里 “拿一个” key;如果都等于 t-1,就把左右兄弟结点和夹在兄弟中间的那个 key 进行合并。

好,下面以几幅图来具体说明上述的步骤和做法:

B - 树

(1)图 1 中,删除 2。

从根节点 a 开始,要下降的结点为 b,发现 b 的 keys 数为 t-1,而它的右兄弟结点 c 的 keys 数也只有 t-1,进行合并。合并后如下图 7。

B - 树

接着在结点 b 中找,要下降的结点为 d,发现 d 的 keys 为 t-1,而它的右兄弟 e 的 keys 是大于 t-1 的,那我们就从 e 里拿一个 key:把 3 放进 d 中,4 放进 b 中。此时 b 结点 keys 数不变,d 变成 3 个,e 少了一个,但依旧满足大于等于 t-1。删除 2 后,如下图 8。

B - 树

如果要下降的那个结点的 keys 数等于 t-1,我们就对其进行填充(或从兄弟里拿一个,或直接合并),使其 keys 数大于 t-1。这样的做法很有用处,且也必须这么做。

为什么 “必须” 呢?

请看图 1,假设我们不在下降的过程中进行填充操作了,现在如果要删除结点 g 中的 13,问题出现了,g 的 keys 数就会小于 t-1,此时违反 B – 树的性质 3。那怎么解决这个问题呢?从兄弟结点 h 中拿一个?不可以,h 的 keys 数也是 t-1。合并?所谓的合并就是把 g,15 和 h 合并为一个结点,可是 c 的 keys 数也是 t-1,也行不通。因此,对要下降的那个 keys 数等于 t-1 的结点进行填充是必要的。

那又如何 “很有用处” 了呢?

观察 “删除 2” 后,图 1 到图 8 的变化,整棵 B – 树的高度降低,高度降低意味着查找效率的提高。分析发现,它之所以会降低,是因为进行了合并操作。读者可以再仔细分析下会发现,这是降低 B – 树高度的唯一方式:根结点一个 key,左右两个孩子结点的 keys 数都是 t-1,合并成一个具有 2t-1 个 keys 的结点。

(2)图 8 中,删除 4。

4 存在于结点 b 中,且 b 不是叶子结点。观察 4 的左右孩子结点(d 和 e)的 keys 数,发现右孩子结点 e 的 keys 数是大于 t-1 的,因此就在 e 中找到 4 的后继(就是 5),用 5 替换 4,接着再在 e 中删除 5。删除后,如下图 9。

B - 树

(3)图 9 中,删除 5。

5 存在于结点 b 中,且 b 不是叶子结点。观察 5 的左右孩子结点(d 和 e)的 keys 数,都是恰好等于 t-1,那么进行合并操作:把结点 d,5 和结点 e 合并为一个结点,再在这个结点中删除 5 即可。删除后,如下图 10。

B - 树

最后看下删除操作的代码实现:

void BTree::merge(Node * node, int i)
{
    Node * cur = node->siblings[i];
    Node * next = node->siblings[i + 1];

    cur->keys[t - 1] = node->keys[i];

    for (int j = 0; j < next->n; j++)
        cur->keys[j + t] = next->keys[j];

    if (!cur->leaf)
    {
        for (int j = 0; j <= next->n; j++)
            cur->siblings[j + t] = next->siblings[j];
    }

    for (int j = i + 1; j < node->n; j++)
        node->keys[j - 1] = node->keys[j];

    for (int j = i + 2; j <= node->n; j++)
        node->siblings[j - 1] = node->siblings[j];

    cur->n += next->n + 1;
    node->n--;

    delete next;
}

void BTree::erase_real(Node * node, int key)
{
    int i = 0;
    while (i < node->n && node->keys[i] < key) // 找到第一个大于等于 key 的位置
        i++;

    if (i < node->n && node->keys[i] == key) // 如果在当前结点找到 key
    {
        if (node->leaf) // 当前结点是叶子的话,直接删除
        {
            for (int j = i + 1; j < node->n; j++)
                node->keys[j - 1] = node->keys[j];

            node->n--;
        }
        else // 当前结点不是叶子的话,分为三种情况
        {
            if (node->siblings[i]->n > t - 1) // 如果 key 的左边那个孩子结点 keys 数大于 t-1 ,就用 key 的前驱替换 key,问题转化为删除前驱
            {
                Node * left = node->siblings[i];

                while (!left->leaf)
                    left = left->siblings[left->n - 1];

                int precursor_key = left->keys[left->n - 1];
                node->keys[i] = precursor_key;

                erase_real(node->siblings[i], precursor_key);
            }
            else if (node->siblings[i + 1]->n > t - 1) // 如果 key 的右边那个孩子结点 keys 数大于 t-1 ,就用 key 的后继替换 key,问题转化为删除后继
            {
                Node * right = node->siblings[i + 1];

                while (!right->leaf)
                    right = right->siblings[0];

                int successor_key = right->keys[0];
                node->keys[i] = successor_key;

                erase_real(node->siblings[i + 1], successor_key);
            }
            else // 如果左右两个孩子结点 keys 都等于 t-1,那就进行合并操作,再删除
            {
                merge(node, i);
                erase_real(node->siblings[i], key);
            }
        }
    }
    else // 如果未在当前结点找到 key
    {
        if (node->leaf) // 是叶子的话,说明根本不存在该 key
            return;

        bool flag = (i == node->n) ? true : false;

        if (node->siblings[i]->n == t - 1) // 每次下降的时候,都要检查下降的那个结点的 keys 数是不是等于下限 t-1,如果是就要做填充处理,分为三种情况
        {
            if (i != 0 && node->siblings[i - 1]->n > t - 1) // 左兄弟结点 keys 数大于 t-1
            {
                Node * cur = node->siblings[i];
                Node * pre = node->siblings[i - 1];

                for (int j = cur->n - 1; j >= 0; j--)
                    cur->keys[j + 1] = cur->keys[j];

                if (!cur->leaf)
                {
                    for (int j = cur->n; j >= 0; j--)
                        cur->siblings[j + 1] = cur->siblings[j];

                    cur->siblings[0] = pre->siblings[pre->n];
                }

                cur->keys[0] = node->keys[i - 1];
                node->keys[i - 1] = pre->keys[pre->n - 1];
                cur->n++;
                pre->n--;
            }
            else if (i != node->n && node->siblings[i + 1]->n > t - 1) // 右兄弟结点 keys 数大于 t-1
            {
                Node * cur = node->siblings[i];
                Node * next = node->siblings[i + 1];

                for (int j = 1; j < next->n; j++)
                    next->keys[j - 1] = next->keys[j];

                if (!next->leaf)
                {
                    for (int j = 1; j < next->n; j++)
                        next->siblings[j - 1] = next->siblings[j];

                    cur->siblings[cur->n + 1] = next->siblings[0];
                }

                cur->keys[cur->n] = node->keys[i];
                node->keys[i] = next->keys[0];
                cur->n++;
                next->n--;
            }
            else // 如果左右兄弟结点 keys 数都等于 t-1,则对它们进行合并
            {
                if (i != node->n)
                    merge(node, i);
                else
                    merge(node, i - 1);
            }
        }

        if (flag && i > node->n)
            erase_real(node->siblings[i - 1], key);
        else
            erase_real(node->siblings[i], key);
    }
}

void BTree::erase(int key)
{
    if (!root)
        return;

    erase_real(root, key);

    if (root->n == 0)
    {
        Node * t = root;

        if (root->leaf)
            root = nullptr;
        else
            root = root->siblings[0];

        delete t;
    }
}

2.4 打印操作

以下实现为层次遍历的代码。

void BTree::print()
{
    if (root)
    {
        queue<Node*> Q;
        Q.push(root);

        while (!Q.empty())
        {
            Node * t = Q.front();
            Q.pop();

            for (int i = 0; i < t->n; i++)
                cout << t->keys[i] << " ";
            cout << endl;

            if (!t->leaf)
            {
                for (int i = 0; i < t->n + 1; i++)
                    Q.push(t->siblings[i]);
            }
        }

        cout << endl;
    }
}

三:完整代码

/**
 *
 * author : 刘毅(Limer)
 * date   : 2017-09-28
 * mode   : C++
 */

#include <iostream>
#include <queue>

using namespace std;

struct Node
{
    bool leaf; // 是否是叶子结点
    int n; // keys 的数目
    int * keys; // 保存 keys
    Node ** siblings; // 保存孩子指针

    Node(int t, bool leaf)
    {
        this->leaf = leaf;
        this->n = 0;
        this->keys = new int[2 * t - 1];
        this->siblings = new Node*[2 * t]{ 0 };
    }
};

class BTree
{
private:
    int t; // Minimum Degree
    Node * root;
private:
    void destroy(Node * node);
    void split_child(Node * parent, int i, Node * child);
    void insert_non_full(Node * node, int key);
    bool find_real(Node * node, int key);
    void merge(Node * node, int i);
    void erase_real(Node * node, int key);
public:
    BTree(int t);
    ~BTree();
    void insert(int key);
    bool find(int key);
    void erase(int key);
    void print();
};

int main()
{
    BTree btree(3);

    // test "insert"
    btree.insert(1);
    btree.insert(3);
    btree.insert(7);
    btree.insert(10);
    btree.insert(11);
    btree.insert(13);
    btree.insert(14);
    btree.insert(15);
    btree.insert(18);
    btree.insert(16);
    btree.insert(19);
    btree.insert(24);
    btree.insert(25);
    btree.insert(26);
    btree.insert(21);
    btree.insert(4);
    btree.insert(5);
    btree.insert(20);
    btree.insert(22);
    btree.insert(2);
    btree.insert(17);
    btree.insert(12);
    btree.insert(6);
    btree.print();

    // test "find"
    cout << ((btree.find(0) == true) ? 1 : -1) << endl;
    cout << ((btree.find(1) == true) ? 1 : -1) << endl;
    cout << ((btree.find(20) == true) ? 1 : -1) << endl << endl;

    // test "erase"
    btree.erase(6);
    btree.print();
    btree.erase(21);
    btree.print();
    btree.erase(20);
    btree.print();
    btree.erase(19);
    btree.print();
    btree.erase(22);
    btree.print();

    return 0;
}

void BTree::destroy(Node * node)
{
    if (node->siblings[0])
    {
        for (int i = 0; i <= node->n; i++)
            destroy(node->siblings[i]);
    }

    delete node;
}

/* 一分为二 */
void BTree::split_child(Node * parent, int i, Node * child)
{
    Node * z = new Node(t, child->leaf);
    z->n = t - 1;

    for (int j = 0; j < t - 1; j++)
        z->keys[j] = child->keys[j + t];

    if (!child->leaf)
    {
        for (int j = 0; j < t; j++)
            z->siblings[j] = child->siblings[j + t];
    }

    child->n = t - 1;

    for (int j = parent->n; j >= i + 1; j--)
        parent->siblings[j + 1] = parent->siblings[j];

    parent->siblings[i + 1] = z;

    for (int j = parent->n - 1; j >= i; j--)
        parent->keys[j + 1] = parent->keys[j];

    parent->keys[i] = child->keys[t - 1];

    parent->n++;
}

void BTree::insert_non_full(Node * node, int key)
{
    int i = node->n - 1;

    if (node->leaf)
    {
        while (i >= 0 && node->keys[i] > key)
        {
            node->keys[i + 1] = node->keys[i];
            i--;
        }

        node->keys[i + 1] = key;
        node->n++;
    }
    else
    {
        while (i >= 0 && node->keys[i] > key)
            i--;

        if (node->siblings[i + 1]->n == 2 * t - 1)
        {
            split_child(node, i + 1, node->siblings[i + 1]);
            if (node->keys[i + 1] < key)
                i++;
        }

        insert_non_full(node->siblings[i + 1], key);
    }
}

bool BTree::find_real(Node * node, int key)
{
    int i = 0;
    while (i < node->n && node->keys[i] < key)
        i++;

    if (node->keys[i] == key)
        return true;

    if (node->leaf)
        return false;

    return find_real(node->siblings[i], key);
}

void BTree::merge(Node * node, int i)
{
    Node * cur = node->siblings[i];
    Node * next = node->siblings[i + 1];

    cur->keys[t - 1] = node->keys[i];

    for (int j = 0; j < next->n; j++)
        cur->keys[j + t] = next->keys[j];

    if (!cur->leaf)
    {
        for (int j = 0; j <= next->n; j++)
            cur->siblings[j + t] = next->siblings[j];
    }

    for (int j = i + 1; j < node->n; j++)
        node->keys[j - 1] = node->keys[j];

    for (int j = i + 2; j <= node->n; j++)
        node->siblings[j - 1] = node->siblings[j];

    cur->n += next->n + 1;
    node->n--;

    delete next;
}

void BTree::erase_real(Node * node, int key)
{
    int i = 0;
    while (i < node->n && node->keys[i] < key) // 找到第一个大于等于 key 的位置
        i++;

    if (i < node->n && node->keys[i] == key) // 如果在当前结点找到 key
    {
        if (node->leaf) // 当前结点是叶子的话,直接删除
        {
            for (int j = i + 1; j < node->n; j++)
                node->keys[j - 1] = node->keys[j];

            node->n--;
        }
        else // 当前结点不是叶子的话,分为三种情况
        {
            if (node->siblings[i]->n > t - 1) // 如果 key 的左边那个孩子结点 keys 数大于 t-1 ,就用 key 的前驱替换 key,问题转化为删除前驱
            {
                Node * left = node->siblings[i];

                while (!left->leaf)
                    left = left->siblings[left->n - 1];

                int precursor_key = left->keys[left->n - 1];
                node->keys[i] = precursor_key;

                erase_real(node->siblings[i], precursor_key);
            }
            else if (node->siblings[i + 1]->n > t - 1) // 如果 key 的右边那个孩子结点 keys 数大于 t-1 ,就用 key 的后继替换 key,问题转化为删除后继
            {
                Node * right = node->siblings[i + 1];

                while (!right->leaf)
                    right = right->siblings[0];

                int successor_key = right->keys[0];
                node->keys[i] = successor_key;

                erase_real(node->siblings[i + 1], successor_key);
            }
            else // 如果左右两个孩子结点 keys 都等于 t-1,那就进行合并操作,再删除
            {
                merge(node, i);
                erase_real(node->siblings[i], key);
            }
        }
    }
    else // 如果未在当前结点找到 key
    {
        if (node->leaf) // 是叶子的话,说明根本不存在该 key
            return;

        bool flag = (i == node->n) ? true : false;

        if (node->siblings[i]->n == t - 1) // 每次下降的时候,都要检查下降的那个结点的 keys 数是不是等于下限 t-1,如果是就要做填充处理,分为三种情况
        {
            if (i != 0 && node->siblings[i - 1]->n > t - 1) // 左兄弟结点 keys 数大于 t-1
            {
                Node * cur = node->siblings[i];
                Node * pre = node->siblings[i - 1];

                for (int j = cur->n - 1; j >= 0; j--)
                    cur->keys[j + 1] = cur->keys[j];

                if (!cur->leaf)
                {
                    for (int j = cur->n; j >= 0; j--)
                        cur->siblings[j + 1] = cur->siblings[j];

                    cur->siblings[0] = pre->siblings[pre->n];
                }

                cur->keys[0] = node->keys[i - 1];
                node->keys[i - 1] = pre->keys[pre->n - 1];
                cur->n++;
                pre->n--;
            }
            else if (i != node->n && node->siblings[i + 1]->n > t - 1) // 右兄弟结点 keys 数大于 t-1
            {
                Node * cur = node->siblings[i];
                Node * next = node->siblings[i + 1];

                for (int j = 1; j < next->n; j++)
                    next->keys[j - 1] = next->keys[j];

                if (!next->leaf)
                {
                    for (int j = 1; j < next->n; j++)
                        next->siblings[j - 1] = next->siblings[j];

                    cur->siblings[cur->n + 1] = next->siblings[0];
                }

                cur->keys[cur->n] = node->keys[i];
                node->keys[i] = next->keys[0];
                cur->n++;
                next->n--;
            }
            else // 如果左右兄弟结点 keys 数都等于 t-1,则对它们进行合并
            {
                if (i != node->n)
                    merge(node, i);
                else
                    merge(node, i - 1);
            }
        }

        if (flag && i > node->n)
            erase_real(node->siblings[i - 1], key);
        else
            erase_real(node->siblings[i], key);
    }
}

BTree::BTree(int t)
{
    this->t = t;
    this->root = nullptr;
}

BTree::~BTree()
{
    if (root)
        destroy(root);
}

void BTree::insert(int key)
{
    if (!root)
    {
        root = new Node(t, true);
        root->keys[0] = key;
        root->n = 1;
    }
    else
    {
        if (root->n == 2 * t - 1)
        {
            Node * s = new Node(t, false);
            s->siblings[0] = root;
            split_child(s, 0, root);

            int i = 0;
            if (s->keys[0] < key)
                i++;

            insert_non_full(s->siblings[i], key);
            root = s;
        }
        else
            insert_non_full(root, key);
    }
}

bool BTree::find(int key)
{
    if (root)
        return find_real(root, key);

    return false;
}

void BTree::erase(int key)
{
    if (!root)
        return;

    erase_real(root, key);

    if (root->n == 0)
    {
        Node * t = root;

        if (root->leaf)
            root = nullptr;
        else
            root = root->siblings[0];

        delete t;
    }
}

void BTree::print()
{
    if (root)
    {
        queue<Node*> Q;
        Q.push(root);

        while (!Q.empty())
        {
            Node * t = Q.front();
            Q.pop();

            for (int i = 0; i < t->n; i++)
                cout << t->keys[i] << " ";
            cout << endl;

            if (!t->leaf)
            {
                for (int i = 0; i < t->n + 1; i++)
                    Q.push(t->siblings[i]);
            }
        }

        cout << endl;
    }
}

运行截图如下:

B - 树

四:参考文献

今天的文章B – 树分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22718.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注