Create B-Tree class--<BTree.h>, <BTNode.h>, with all tests passed. Tests are stored in <test_BTree.cpp>
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -20,3 +20,5 @@ m阶B树,即m路平衡搜索树。分支数上界为m,下界为[m/2]取上
|
||||
## B树的插入
|
||||
|
||||
B树高度增加的唯一情况,与B树根节点可以只拥有两个分支,之间的关系
|
||||
|
||||
由于只有上溢才能导致B树高度增加,B树的内部结点不可能有指向`nullptr`的指针
|
||||
|
||||
23
thu_dsa/chp8/BTNode.h
Normal file
23
thu_dsa/chp8/BTNode.h
Normal 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
194
thu_dsa/chp8/BTree.h
Normal 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
210
thu_dsa/chp8/test_BTree.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user