Реализация B * -Trees в C ++
B * -дерево порядка m - это дерево поиска, которое либо пусто, либо удовлетворяет трем свойствам:
- У корневого узла минимум два и максимум два этажа ((2m-2) / 3) +1 детей
- Остальные внутренние узлы имеют минимальный этаж ((2m-1) / 3) и максимальный m дочерних элементов.
- Все внешние узлы находятся на одном уровне.
Преимущество использования B *-деревьев перед B-деревьями - это уникальная особенность, называемая разделением «два на три». Таким образом, минимальное количество ключей в каждом узле составляет не половину максимального числа, а две трети от него, что делает данные намного более компактными. Однако недостатком этого является сложная операция удаления.
Трудности практической реализации алгоритма B-star способствуют тому, что он не так регулярно используется, как его аналоги B и B +.
Ниже представлена базовая реализация функции вставки B-звезды - просто чтобы продемонстрировать ее отличие от B (полная реализация была бы гораздо более длительной и сложной).
Уникальные части алгоритма вставки B * Tree заключаются в следующем:
Два-три сплита
1. При вставке в полный листовой узел (который не является корневым) и который имеет полного правого брата (и чей родительский узел имеет хотя бы один свободный ключ):
- Возьмите массив ('marray'), состоящий из ключей 'm-1' полного листового узла, родительского ключа этого узла, нового ключа, который нужно вставить, и ключей 'm-1' его правого брата ( Всего m-1 + 1 + 1 + m-1 = 2m ключей)
- Отсортируйте эти ключи
- Создайте три новых узла:
- p - чьи ключи являются первыми (2m - 2) / 3 элементами 'marray'
Элемент с индексом (2m - 2) / 3 сохраняется как 'parent1' - q - чьи ключи являются следующими (2m - 1) / 3 элементами 'marray' после parent1
Элемент с индексом (4m) / 3 сохраняется как parent2. - r - чьи ключи являются последними (2m) / 3 элементами 'marray' после parent2
- p - чьи ключи являются первыми (2m - 2) / 3 элементами 'marray'
- Ключ в родительском элементе листа, который указывает на этот лист, должно иметь значение, замененное на 'parent1'.
- Если у родительского ключа в iv) есть какие-либо смежные ключи, их следует сдвинуть вправо. В оставшееся пространство поместите parent2.
- p, q и r должны быть дочерними ключами parent1 и parent2 (если parent1 и parent2 - первые два ключа в родительском узле), иначе p, q, r должны быть дочерними ключами ключа перед родитель 1, родитель 1 и родитель 2 соответственно.
Перед вставкой:
После прошивки:
2. При вставке в полный листовой узел (который не является корневым) с пустым / неполным правым братом.
3. Остальные случаи такие же, как для B-деревьев.
Рекомендуется: сначала попробуйте свой подход в {IDE}, прежде чем переходить к решению.
Примеры:
Input: Add 4 to 1 2 3 L 5 R 7 8 9
Output: 1 2 L 3 7 R 4 5 R 8 9
3 and 7 become the parent keys by the two-three splitInput : Add 5 to 2 3 4 L 6 R 8 9 11
Output : 2 3 L 4 8 R 5 6 R 9 11
3 and 6 become the parent keys by the two-three split
Ниже представлена реализация описанного выше подхода:
// CPP program to implement B* tree #include <bits/stdc++.h> using namespace std; // This can be changed to any value - // it is the order of the B* Tree #define N 4 struct node { // key of N-1 nodes int key[N - 1]; // Child array of 'N' length struct node* child[N]; // To state whether a leaf or not; if node // is a leaf, isleaf=1 else isleaf=0 int isleaf; // Counts the number of filled keys in a node int n; // Keeps track of the parent node struct node* parent; }; // This function searches for the leaf // into which to insert element 'k' struct node* searchforleaf( struct node* root, int k, struct node* parent, int chindex) { if (root) { // If the passed root is a leaf node, then // k can be inserted in this node itself if (root->isleaf == 1) return root; // If the passed root is not a leaf node, // implying there are one or more children else { int i; /*If passed root's initial key is itself g reater than the element to be inserted, we need to insert to a new leaf left of the root*/ if (k < root->key[0]) root = searchforleaf(root->child[0], k, root, 0); else { // Find the first key whose value is greater // than the insertion value // and insert into child of that key for (i = 0; i < root->n; i++) if (root->key[i] > k) root = searchforleaf(root->child[i], k, root, i); // If all the keys are less than the insertion // key value, insert to the right of last key if (root->key[i - 1] < k) root = searchforleaf(root->child[i], k, root, i); } } } else { // If the passed root is NULL (there is no such // child node to search), then create a new leaf // node in that location struct node* newleaf = new node; struct newleaf->isleaf = 1; newleaf->n = 0; parent->child[chindex] = newleaf; newleaf->parent = parent; return newleaf; } } struct node* insert( struct node* root, int k) { if (root) { struct node* p = searchforleaf(root, k, NULL, 0); struct node* q = NULL; int e = k; // If the leaf node is empty, simply // add the element and return for ( int e = k; p; p = p->parent) { if (p->n == 0) { p->key[0] = e; p->n = 1; return root; } // If number of filled keys is less than maximum if (p->n < N - 1) { int i; for (i = 0; i < p->n; i++) { if (p->key[i] > e) { for ( int j = p->n - 1; j >= i; j--) p->key[j + 1] = p->key[j]; break ; } } p->key[i] = e; p->n = p->n + 1; return root; } // If number of filled keys is equal to maximum // and it's not root and there is space in the parent if (p->n == N - 1 && p->parent && p->parent->n < N) { int m; for ( int i = 0; i < p->parent->n; i++) if (p->parent->child[i] == p) { m = i; break ; } // If right sibling is possible if (m + 1 <= N - 1) { // q is the right sibling q = p->parent->child[m + 1]; if (q) { // If right sibling is full if (q->n == N - 1) { struct node* r = new node; struct int * z = new int [((2 * N) / 3)]; int parent1, parent2; int * marray = new int [2 * N]; int i; for (i = 0; i < p->n; i++) marray[i] = p->key[i]; int fege = i; marray[i] = e; marray[i + 1] = p->parent->key[m]; for ( int j = i + 2; j < ((i + 2) + (q->n)); j++) marray[j] = q->key[j - (i + 2)]; // marray=bubblesort(marray, 2*N) // a more rigorous implementation will // sort these elements // Put first (2*N-2)/3 elements into keys of p for ( int i = 0; i < (2 * N - 2) / 3; i++) p->key[i] = marray[i]; parent1 = marray[(2 * N - 2) / 3]; // Put next (2*N-1)/3 elements into keys of q for ( int j = ((2 * N - 2) / 3) + 1; j < (4 * N) / 3; j++) q->key[j - ((2 * N - 2) / 3 + 1)] = marray[j]; parent2 = marray[(4 * N) / 3]; // Put last (2*N)/3 elements into keys of r for ( int f = ((4 * N) / 3 + 1); f < 2 * N; f++) r->key[f - ((4 * N) / 3 + 1)] = marray[f]; // Because m=0 and m=1 are children of the same key, // a special case is made for them if (m == 0 || m == 1) { p->parent->key[0] = parent1; p->parent->key[1] = parent2; p->parent->child[0] = p; p->parent->child[1] = q; p->parent->child[2] = r; return root; } else { p->parent->key[m - 1] = parent1; p->parent->key[m] = parent2; p->parent->child[m - 1] = p; p->parent->child[m] = q; p->parent->child[m + 1] = r; return root; } } } else // If right sibling is not full { int put; if (m == 0 || m == 1) put = p->parent->key[0]; else put = p->parent->key[m - 1]; for ( int j = (q->n) - 1; j >= 1; j--) q->key[j + 1] = q->key[j]; q->key[0] = put; p->parent->key[m == 0 ? m : m - 1] = p->key[p->n - 1]; } } } } /*Cases of root splitting, etc. are omitted as this implementation is just to demonstrate the two-three split operation*/ } else { // Create new node if root is NULL struct node* root = new node; struct root->key[0] = k; root->isleaf = 1; root->n = 1; root->parent = NULL; } } // Driver code int main() { /* Consider the following tree that has been obtained from some root split: 6 / 1 2 4 7 8 9 We wish to add 5. This makes the B*-tree: 4 7 / 1 2 5 6 8 9 Contrast this with the equivalent B-tree, in which some nodes are less than half full 4 6 / 1 2 5 7 8 9 */ // Start with an empty root struct node* root = NULL; // Insert 6 root = insert(root, 6); // Insert 1, 2, 4 to the left of 6 root->child[0] = insert(root->child[0], 1); root->child[0] = insert(root->child[0], 2); root->child[0] = insert(root->child[0], 4); root->child[0]->parent = root; // Insert 7, 8, 9 to the right of 6 root->child[1] = insert(root->child[1], 7); root->child[1] = insert(root->child[1], 8); root->child[1] = insert(root->child[1], 9); root->child[1]->parent = root; cout << "Original tree: " << endl; for ( int i = 0; i < root->n; i++) cout << root->key[i] << " " ; cout << endl; for ( int i = 0; i < 2; i++) { cout << root->child[i]->key[0] << " " ; cout << root->child[i]->key[1] << " " ; cout << root->child[i]->key[2] << " " ; } cout << endl; cout << "After adding 5: " << endl; // Inserting element '5': root->child[0] = insert(root->child[0], 5); // Printing nodes for ( int i = 0; i <= root->n; i++) cout << root->key[i] << " " ; cout << endl; for ( int i = 0; i < N - 1; i++) { cout << root->child[i]->key[0] << " " ; cout << root->child[i]->key[1] << " " ; } return 0; } |
Выход:
Исходное дерево: 6 1 2 4 7 8 9 После добавления 5: 4 7 1 2 5 6 8 9
Вниманию читателя! Не прекращайте учиться сейчас. Освойте все важные концепции DSA с помощью самостоятельного курса DSA по доступной для студентов цене и будьте готовы к работе в отрасли. Чтобы завершить подготовку от изучения языка к DS Algo и многому другому, см. Полный курс подготовки к собеседованию .
Если вы хотите посещать живые занятия с отраслевыми экспертами, пожалуйста, обращайтесь к Geeks Classes Live и Geeks Classes Live USA.