我在大学有一个分配,我必须实现一个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();
}
我没有测试任何与删除有关的内容,所以我不知道其他案例是否运作良好。唯一经过测试的是插入。 除此之外,我真的不知道我的密钥解析是否有用(我认为我的算法会跳过每个节点的最后一个密钥,但我现在无法测试它。)
感谢任何反馈。谢谢! :)