B-Tree“案例3”问题

时间:2017-07-06 18:56:33

标签: c++ algorithm b-tree

我在大学有一个分配,我必须实现一个B树(没有写入磁盘部分 - 所有它都留在内存中)。我已实现插入,但我有删除问题。更确切地说,我遇到了第三种情况的问题(http://imgur.com/a/dNAZL或参见算法简介第499页了解更多详情。)

我真的不明白哪些键应该移动到哪里以及如何决定。我发现算法导论中的解释非常模糊,因为它留下了非常大的解释空间。 这是我到目前为止所得到的:

// B-Tree.h
#pragma once
#include <list>
#include <vector>

using namespace std;

template <class T>
class Node
{
public:
    unsigned nr_keys = 0;
    T *keys = nullptr;
    Node<T> **children = nullptr;
    bool leaf = false;

    Node(unsigned nr_keys)
    {
        keys = new T[nr_keys]();
        children = new Node<T>*[nr_keys + 1]();
    }

    ~Node()
    {
        if (keys)
            delete[] keys;
        if (children)
            delete[] children;
    }
};

template <class T>
class BTree
{
private:
    Node<T> *root = nullptr;
    unsigned t = 0, keys_min = 0, keys_max = 0;

    void split_child(Node<T> *parent, unsigned child_index)
    {
        Node<T> *child = parent->children[child_index];

        // creating the new child node
        Node<T> *new_child = new Node<T>(keys_max);
        new_child->leaf = child->leaf;

        // moving the right child keys to the new child
        child->nr_keys = keys_min;
        new_child->nr_keys = keys_min;
        for (unsigned i = 0; i < keys_min; i++)
            new_child->keys[i] = child->keys[i + t];

        // moving the right children to the new child
        if (!child->leaf)
            for (unsigned i = 0; i < keys_min + 1; i++)
                new_child->children[i] = child->children[i + keys_min + 1];

        // moving the parent right keys to make room for the new key
        for (unsigned i = parent->nr_keys; i > child_index; i--)
            parent->keys[i] = parent->keys[i - 1];

        // inserting the new key
        parent->keys[child_index] = child->keys[keys_min];
        parent->nr_keys++;

        // moving the parent right children to make room for the new child
        for (unsigned i = parent->nr_keys + 1; i > child_index; i--)
            parent->children[i] = parent->children[i - 1];

        // inserting the new child
        parent->children[child_index + 1] = new_child;
    }

    void insert_rootnotfull(Node<T> *node, T key)
    {
        // i = the last key
        unsigned i = node->nr_keys - 1;

        // if we are on a leaf we just insert the element
        if (node->leaf)
        {
            // we move the elements while our key is smaller than the current key
            while (i >= 0 && key < node->keys[i])
            {
                node->keys[i + 1] = node->keys[i];
                i--;
            }

            // in the empty space we put the new key
            node->keys[i + 1] = key;

            // we increment the number of keys found in the node
            node->nr_keys++;
        }

        // otherwise we have to get to a leaf
        else
        {
            // we find the child in which we should place our key
            while (i >= 0 && key < node->keys[i])
                i--;

            // fix the overshoot
            i++;

            // if the child is full, we split it
            if (node->children[i]->nr_keys == keys_max)
            {
                split_child(node, i);

                // if the key that has been pushed up is smaller than our key, we go one key further
                if (key > node->keys[i])
                    i++;
            }

            // try to insert the key in the found and split child
            insert_rootnotfull(node->children[i], key);
        }
    }

public:
    BTree(unsigned t)
    {
        this->t = t;
        keys_min = t - 1;
        keys_max = 2 * t - 1;

        root = new Node<T>(keys_max);
        root->leaf = true;
    }

    void print()
    {
        vector<Node<T>*> vect;
        vect.emplace_back(root);

        while (!vect.empty())
        {
            unsigned i, j, size = vect.size();
            for (i = 0; i < size; i++)
            {
                for (j = 0; j < vect[i]->nr_keys; j++)
                {
                    cout << vect[i]->keys[j] << ' ';
                    if(!vect[i]->leaf)
                        vect.emplace_back(vect[i]->children[j]);
                }
                if (!vect[i]->leaf)
                    vect.emplace_back(vect[i]->children[j]);
                cout << "          ";
            }
            vect.erase(vect.begin(), vect.begin()+size);
            cout << endl;
        }
    }

    // returns true if removal has been successful and false if not
    bool remove(Node<T> *current_node, T key)
    {
        // i = key index
        //unsigned i = findindex(current_node, key);
        unsigned i = 0;
        while (i < current_node.nr_keys && key > current_node.keys[i])
            i++;
        // cases 1 and 2
        if (i < current_node->nr_keys && key == current_node->keys[i])
            // case 1 (if the key is in the current_node and current_node is a leaf, delete key from current_node)
            if (current_node->leaf)
            {
                // move the right side elements of keys[i] to the left by one position (over the current element)
                for (int j = i; j < current_node->nr_keys - 1; j++)
                    current_node->keys[j] = current_node->keys[j + 1];
                // decrease the number of keys by one
                current_node->nr_keys--;
                return true;
            }
            // case 2 (if the key is in the current_node and current_node is internal)
            else
            {
                // case 2a (if the child that precedes key in current_node has at least keys_min+1 keys)
                if (current_node->children[i]->nr_keys >= keys_min + 1)
                {
                    // find the predecessor of key starting from key's left child
                    Node<T> *predecessor_node = current_node->children[i];
                    // the predecessor will be the lowest and most right key starting from key's left child
                    while (!predecessor_node->leaf)
                        predecessor_node = predecessor_node->children[predecessor_node->nr_keys];
                    T predecessor = predecessor_node->keys[predecessor_node->nr_keys - 1];
                    // delete the predecessor
                    remove(predecessor_node, predecessor);
                    // replace key from current_node with predecessor
                    current_node->keys[i] = predecessor;
                    return true;
                }
                // case 2b (if the child that precedes key in current_node doesn't have at least keys_min+1 keys but the child that succedes key has at least keys_min+1 keys)
                else if (current_node->children[i+1]->nr_keys >= keys_min + 1)
                {
                    // find the successor of key starting from key's right child
                    Node<T> *successor_node = current_node->children[i+1];
                    // the successor will be the lowest and most left key starting from key's right child
                    while (!successor_node->leaf)
                        successor_node = successor_node->children[0];
                    T successor = successor_node->keys[0];
                    // delete the successor
                    remove(successor_node, successor);
                    // replace key from current_node with successor
                    current_node->keys[i] = successor;
                    return true;
                }
                // case 2c (both the child that precedes key and the child that succedes key have only keys_min keys)
                else
                {
                    // merge key and the child that succedes key into the child that precedes key (which now contains max_keys keys)
                    Node<T> *predecessor_node = current_node->children[i];
                    Node<T> *successor_node = current_node->children[i + 1];
                    predecessor_node->keys[predecessor_node->nr_keys] = key;
                    // this causes the last child (children[nr_keys]) to be empty
                    predecessor_node->nr_keys++;
                    for (int i = 0; i < successor_node->nr_keys; i++)
                    {
                        predecessor_node->keys[predecessor_node->nr_keys + i] = successor_node->keys[i];
                        // because the last child is empty initially (see above comment), we begin with it
                        predecessor_node->children[predecessor_node->nr_keys + i] = successor_node->children[i];
                    }
                    predecessor_node->nr_keys += successor_node->nr_keys;
                    // we insert the supplementar child
                    predecessor_node->children[predecessor_node->nr_keys] = successor_node->children[successor_node->nr_keys];

                    // remove key's right child
                    delete successor_node;

                    // remove the key and its right child from current_node
                    for (int j = i; j < current_node->nr_keys-1; j++)
                    {
                        current_node->keys[j] = current_node->keys[j+1];
                        current_node->children[j+1] = current_node->children[j+2];
                    }
                    current_node->nr_keys--;

                    // recursively remove key from predecessor_node
                    remove(predecessor_node, key);
                }
            }
        // case 3 (key is not present in current_node)
        else
            // the key is not present in the tree
            if (current_node->leaf)
                return false;
            // case 3a and 3b
            else if (current_node->children[i]->nr_keys == keys_min)
            {
                // case 3a - borrow from previous
                if (i > 0 && current_node->children[i - 1]->nr_keys == keys_min + 1)
                {
                    Node<T> *child = current_node->children[i];
                    Node<T> *sibling = current_node->children[i - 1];

                    // current key comes down into the child
                    child->keys[child->nr_keys] = current_node->keys[i];
                    child->nr_keys++;

                }
                // case 3a - borrow from next
                else if (i < current_node->nr_keys && current_node->children[i + 1]->nr_keys == keys_min + 1)
                {

                }
                // case 3b - merge
                else
                {
                    // verify if root is empty and replace it with first child if so
                    if (root->nr_keys == 0)
                    {
                        Node<T> *temp = root->children[0];
                        delete root;
                        root = temp;
                    }
                }
            }
            else
            {
                remove(current_node->children[i], key);
            }
    }
    void insert(T key)
    {
        // if the root is full, we need to make another one that can accept our element
        if (root->nr_keys == keys_max)
        {
            // create a new root
            Node<T> *new_root = new Node<T>(keys_max);

            // the first child of the new root is the old root
            new_root->children[0] = root;

            // the root is now the new root
            root = new_root;

            // we now split the first child of the new root (the old root)
            split_child(root, 0);
        }

        // we can now insert the new element
        insert_rootnotfull(root, key);
    }
    pair<Node<T>, unsigned> search(Node<T> *current_node, T key)
    {
        unsigned i = 0;
        while (i < current_node.nr_keys && key < current_node.keys[i])
            i++;
        if (i < current_node.nr_keys && k == current_node.keys[i])
            return pair<Node<T>, unsigned>(current_node, i);
        else
            if (current_node.leaf)
                return nullptr;
            else
                search(current_node.children[i], key);
    }
};

// Source.cpp
#include <iostream>
#include "B-Tree.h"

using namespace std;

void main()
{
    BTree<char> bt(3);
    char temp;
    cin >> temp;
    while (temp != '0')
    {
        bt.insert(temp);
        cin >> temp;
    }
    bt.print();
}

我没有测试任何与删除有关的内容,所以我不知道其他案例是否运作良好。唯一经过测试的是插入。 除此之外,我真的不知道我的密钥解析是否有用(我认为我的算法会跳过每个节点的最后一个密钥,但我现在无法测试它。)

感谢任何反馈。谢谢! :)

0 个答案:

没有答案