Create B-Tree class--<BTree.h>, <BTNode.h>, with all tests passed. Tests are stored in <test_BTree.cpp>

This commit is contained in:
Shine wOng
2019-06-16 12:56:10 +08:00
parent 6552163354
commit 9f25b126dc
5 changed files with 430 additions and 0 deletions

View File

@@ -47,6 +47,7 @@ public:
void print(void);
int find(T const &elem);
int find(T const &elem, int lo, int hi);
int search(T const &elem) { return search(elem, 0, _size); }
int search(T const &elem, int lo, int hi);
int fib_search(T const &elem, int lo, int hi);
int binary_search(T const &elem, int lo, int hi);

View File

@@ -20,3 +20,5 @@ m阶B树即m路平衡搜索树。分支数上界为m下界为[m/2]取上
## B树的插入
B树高度增加的唯一情况与B树根节点可以只拥有两个分支之间的关系
由于只有上溢才能导致B树高度增加B树的内部结点不可能有指向`nullptr`的指针

23
thu_dsa/chp8/BTNode.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef BTNODE_H_
#define BTNODE_H_
#include "../chp2/Vector.h"
#define BTNodePosi(T) BTNode<T>*
template <typename T>
class BTNode{
public:
Vector<T> keys;
Vector<BTNodePosi(T)> children;
BTNodePosi(T) parent;
BTNode() { children.push_back(nullptr); }
BTNode(T const &key, BTNodePosi(T) left = nullptr, BTNodePosi(T) right = nullptr){
keys.push_back(key);
children.push_back(left);
children.push_back(right);
}
};
#endif

194
thu_dsa/chp8/BTree.h Normal file
View File

@@ -0,0 +1,194 @@
#ifndef BTREE_H_
#define BTREE_H_
#include "BTNode.h"
template <typename T>
class BTree{
protected:
BTNodePosi(T) __root;
BTNodePosi(T) __hot;
int __order = 3; //default to 2-3 Btree
int __size = 0;
//protected methods
void solveOverflow(BTNodePosi(T) x);
void solveUnderflow(BTNodePosi(T) x);
public:
//constructor
BTree() { __root = new BTNode<T>(); }
BTree(int order) : __order{ order } { __root = new BTNode<T>(); }
//public interfaces
int getSize() { return __size; }
BTNodePosi(T) root() { return __root; }
BTNodePosi(T) search(T const &key);
bool insert(T const &key);
bool remove(T const &key);
};
//protected methods
template <typename T>
void BTree<T>::solveOverflow(BTNodePosi(T) x){
while (x->keys.getSize() == __order) {
int mid = __order / 2;
BTNodePosi(T) newLeaf = new BTNode<T>();
BTNodePosi(T) p = x->parent;
//construct split node
int ix = mid + 1;
newLeaf->children[0] = x->children[ix];
if (x->children[ix]) x->children[ix]->parent = newLeaf;
for(;ix != __order; ++ix){
newLeaf->keys.push_back(x->keys[ix]);
newLeaf->children.push_back(x->children[ix + 1]);
if (x->children[ix + 1]) x->children[ix + 1]->parent = newLeaf;
}
if(!p){//construct new __root
__root = new BTNode<T>(x->keys[mid], x, newLeaf);
x->parent = __root;
newLeaf->parent = __root;
p = __root;
}
else {
int pos = p->keys.search(x->keys[mid]);
p->keys.insert(pos + 1, x->keys[mid]);
p->children.insert(pos + 2, newLeaf);
newLeaf->parent = p;
}
x->keys.pop(mid, __order);
x->children.pop(mid + 1, __order + 1);
x = p;
}
}
template <typename T>
void BTree<T>::solveUnderflow(BTNodePosi(T) x){
while (x != __root && x->children.getSize() == ((__order - 1) >> 1)) {
BTNodePosi(T) p = x->parent;
int pos = 0;
while (p->children[pos] != x) ++pos;
BTNodePosi(T) leftSibling = (pos != 0 ? p->children[pos - 1] : nullptr);
BTNodePosi(T) rightSibling = (pos != p->children.getSize() - 1 ? p->children[pos + 1] : nullptr);
//look around left and right
if (leftSibling && leftSibling->children.getSize() > ((__order + 1) >> 1)) {
x->keys.insert(0, p->keys[pos - 1]);
x->children.insert(0, leftSibling->children.pop_back());
if (x->children[0]) x->children[0]->parent = x;
p->keys[pos - 1] = leftSibling->keys.pop_back();
return;
}
else
if (rightSibling && rightSibling->children.getSize() > ((__order + 1) >> 1)) {
x->keys.push_back(p->keys[pos + 1]);
x->children.push_back(rightSibling->children.pop(0));
if (x->children[x->children.getSize() - 1]) x->children[x->children.getSize() - 1]->parent = x;
p->keys[pos + 1] = rightSibling->keys.pop(0);
return;
}
else {
if (leftSibling) {//merge left sibling
leftSibling->keys.push_back(p->keys[pos - 1]);
int ix = 0;
for (; ix != x->keys.getSize(); ++ix) {
leftSibling->keys.push_back(x->keys[ix]);
leftSibling->children.push_back(x->children[ix]);
if (x->children[ix]) x->children[ix]->parent = leftSibling;
}
leftSibling->children.push_back(x->children[ix]);
if (x->children[ix]) x->children[ix]->parent = leftSibling;
p->keys.pop(pos - 1);
p->children.pop(pos);
delete x;
}
else { //merge right sibling
x->keys.push_back(p->keys[pos]);
int ix = 0;
for (; ix != rightSibling->keys.getSize(); ++ix) {
x->keys.push_back(rightSibling->keys[ix]);
x->children.push_back(rightSibling->children[ix]);
if (rightSibling->children[ix]) rightSibling->children[ix]->parent = x;
}
x->children.push_back(rightSibling->children[ix]);
if (rightSibling->children[ix]) rightSibling->children[ix]->parent = x;
p->keys.pop(pos);
p->children.pop(pos + 1);
delete rightSibling;
}
if (p == __root && p->keys.getSize() == 0) {
__root = p->children[0];
__root->parent = 0;
p = __root;
}
x = p;
}
}
}
//public interfaces
template <typename T>
BTNodePosi(T) BTree<T>::search(T const &key){
BTNodePosi(T) x = __root;
__hot = nullptr;
int pos;
while(x){
pos = x->keys.search(key);
if (pos != -1 && x->keys[pos] == key) break;
//else
__hot = x;
x = x->children[pos + 1];
}
return x;
}
template <typename T>
bool BTree<T>::insert(T const &key){
BTNodePosi(T) x = search(key);
if (x) return false;
//else
x = __hot;
int pos = x->keys.search(key);
x->keys.insert(pos + 1 , key);
x->children.push_back(nullptr);
++__size;
solveOverflow(x);
return true;
}
template <typename T>
bool BTree<T>::remove(T const &key){
BTNodePosi(T) x = search(key);
if (!x) return false;
//else
int pos = x->keys.search(key);
if(x->children[0]){ //if x is not a leaf node, find x's succ
BTNodePosi(T) succ = x->children[pos + 1];
while (succ->children[0]) succ = succ->children[0];
x->keys[pos] = succ->keys[0];
x = succ;
pos = 0;
}
//now x is a leaf node
x->keys.pop(pos);
x->children.pop_back();
--__size;
solveUnderflow(x);
return true;
}
#endif

210
thu_dsa/chp8/test_BTree.cpp Normal file
View File

@@ -0,0 +1,210 @@
#include "BTree.h"
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
void test_insert_and_search();
void test_remove();
int main(){
cout << "Running tests." << endl;
test_insert_and_search();
test_remove();
cout << "All tests passed." << endl;
system("pause");
return 0;
}
void test_insert_and_search(){
BTree<int> tree(4);
int insert_sequence[] = { 25, 36, 17, 48, 32, 5, 67, 72, 99, 19, 22 };
int ix = 0;
//insert __root
assert(tree.getSize() == 0);
assert(tree.insert(insert_sequence[ix++]));
assert(tree.getSize() == 1);
assert(tree.root()->keys[0] == 25);
assert(tree.root()->keys.getSize() == 1);
assert(tree.root()->parent == nullptr);
//insert duplicate key
assert(!tree.insert(25));
assert(tree.getSize() == 1);
assert(tree.root()->keys[0] == 25);
assert(tree.root()->keys.getSize() == 1);
assert(tree.root()->children[0] == nullptr);
assert(tree.root()->children.getSize() == 2);
assert(tree.root()->parent == nullptr);
//insert later keys
assert(tree.insert(insert_sequence[ix++]));
assert(tree.getSize() == 2);
assert(tree.root()->keys[0] == 25);
assert(tree.root()->keys[1] == 36);
assert(tree.root()->keys.getSize() == 2);
assert(tree.root()->children[0] == nullptr);
assert(tree.root()->children.getSize() == 3);
assert(tree.root()->parent == nullptr);
//first split
assert(tree.insert(insert_sequence[ix++]));
assert(tree.insert(insert_sequence[ix++]));
assert(tree.root()->keys[0] == 36);
assert(tree.root()->keys.getSize() == 1);
assert(tree.root()->children.getSize() == 2);
assert(tree.root()->parent == nullptr);
auto first = tree.root()->children[0];
auto second = tree.root()->children[1];
assert(first->keys.getSize() == 2);
assert(first->children.getSize() == 3);
assert(first->children[0] == nullptr);
assert(first->parent == tree.root());
assert(second->keys.getSize() == 1);
assert(second->children.getSize() == 2);
assert(second->children[0] == nullptr);
assert(second->parent == tree.root());
//consecutive insert
for (; ix != 11; ++ix)
assert(tree.insert(insert_sequence[ix]));
assert(tree.getSize() == 11);
auto root = tree.root();
assert(root->keys[0] == 36);
assert(root->keys.getSize() == 1);
assert(root->children.getSize() == 2);
assert(root->parent == nullptr);
first = root->children[0];
assert(first->keys.getSize() == 2);
assert(first->keys[0] == 19 && first->keys[1] == 25);
assert(first->children.getSize() == 3);
assert(first->parent == root);
second = root->children[1];
assert(second->keys.getSize() == 1);
assert(second->keys[0] == 72);
assert(second->children.getSize() == 2);
assert(second->parent == root);
auto firstLeft = first->children[0];
assert(firstLeft->keys.getSize() == 2);
assert(firstLeft->keys[0] == 5 && firstLeft->keys[1] == 17);
assert(firstLeft->children[0] == nullptr && firstLeft->children.getSize() == 3);
assert(firstLeft->parent == first);
auto firstMid = first->children[1];
assert(firstMid->keys.getSize() == 1);
assert(firstMid->keys[0] == 22);
assert(firstMid->children.getSize() == 2 && firstMid->children[0] == nullptr);
assert(firstMid->parent == first);
auto firstRight = first->children[2];
assert(firstRight->keys.getSize() == 1);
assert(firstRight->keys[0] == 32);
assert(firstRight->children.getSize() == 2 && firstRight->children[0] == nullptr);
assert(firstRight->parent == first);
auto secondLeft = second->children[0];
assert(secondLeft->keys.getSize() == 2);
assert(secondLeft->keys[0] == 48 && secondLeft->keys[1] == 67);
assert(secondLeft->children.getSize() == 3 && secondLeft->children[0] == nullptr);
assert(secondLeft->parent == second);
auto secondRight = second->children[1];
assert(secondRight->keys.getSize() == 1);
assert(secondRight->keys[0] == 99);
assert(secondRight->children.getSize() == 2 && secondRight->children[0] == nullptr);
assert(secondLeft->parent == second && secondRight->parent == second);
//test duplicate insert
for (ix = 0; ix != 11; ++ix)
assert(!tree.insert(insert_sequence[ix]));
assert(tree.getSize() == 11);
}
void test_remove(){
BTree<int> tree(4);
int insert_sequence[] = { 25, 36, 17, 48, 32, 5, 67, 72, 99, 19, 22 };
int ix = 0;
//insert and remove
tree.insert(7);
assert(!tree.remove(14));
assert(tree.remove(7));
assert(tree.getSize() == 0);
assert(tree.root()->keys.getSize() == 0);
assert(tree.root()->children.getSize() == 1);
assert(tree.root()->children[0] == nullptr);
//look around left and right
for (; ix != 4; ++ix)
tree.insert(insert_sequence[ix]);
assert(tree.remove(36));
assert(tree.getSize() == 3);
assert(tree.root()->keys[0] == 25);
assert(tree.root()->keys.getSize() == 1);
assert(tree.root()->children.getSize() == 2);
auto first = tree.root()->children[0];
auto second = tree.root()->children[1];
assert(first->keys.getSize() == 1 && first->keys[0] == 17);
assert(first->children.getSize() == 2 && first->children[0] == nullptr);
assert(second->keys.getSize() == 1 && second->keys[0] == 48);
assert(second->children.getSize() == 2 && second->children[0] == nullptr);
//merge left to root
assert(tree.remove(25));
assert(tree.getSize() == 2);
assert(tree.root()->keys[0] == 17 && tree.root()->keys[1] == 48);
assert(tree.root()->keys.getSize() == 2);
assert(tree.root()->children.getSize() == 3);
assert(tree.root()->children[0] == nullptr);
//clear tree
assert(tree.remove(17));
assert(tree.remove(48));
assert(tree.getSize() == 0);
//merge right to root
for(ix = 0; ix != 4; ++ix)
tree.insert(insert_sequence[ix]);
tree.remove(36);
assert(tree.remove(17));
assert(tree.getSize() == 2);
assert(tree.root()->keys[0] == 25 && tree.root()->keys[1] == 48);
assert(tree.root()->keys.getSize() == 2);
assert(tree.root()->children.getSize() == 3);
assert(tree.root()->children[0] == nullptr);
//clear tree
assert(tree.remove(25));
assert(tree.remove(48));
assert(tree.getSize() == 0);
//general tests
for (ix = 0; ix != 11; ++ix)
tree.insert(insert_sequence[ix]);
assert(tree.remove(48));
assert(tree.getSize() == 10);
assert(tree.remove(36));
auto root = tree.root();
assert(root->keys.getSize() == 1 && root->keys[0] == 25);
assert(root->children.getSize() == 2);
auto left = root->children[0];
auto right = root->children[1];
assert(left->keys.getSize() == 1 && left->keys[0] == 19);
assert(left->children.getSize() == 2);
assert(right->keys.getSize() == 1 && right->keys[0] == 67);
assert(right->children.getSize() == 2);
auto leftleft = left->children[0];
auto leftright = left->children[1];
assert(leftleft->keys.getSize() == 2 && leftleft->keys[0] == 5 && leftleft->keys[1] == 17);
assert(leftleft->children.getSize() == 3 && leftleft->children[0] == nullptr);
assert(leftright->keys.getSize() == 1 && leftright->keys[0] == 22);
assert(leftright->children.getSize() == 2 && leftright->children[0] == nullptr);
auto rightleft = right->children[0];
auto rightright = right->children[1];
assert(rightright->keys.getSize() == 2 && rightright->keys[0] == 72 && rightright->keys[1] == 99);
assert(rightright->children.getSize() == 3 && rightright->children[0] == nullptr);
assert(rightleft->keys.getSize() == 1 && rightleft->keys[0] == 32);
assert(rightleft->children.getSize() == 2 && leftright->children[0] == nullptr);
}