diff --git a/thu_dsa/chp2/Vector.h b/thu_dsa/chp2/Vector.h index 488bfee..0f4abab 100644 --- a/thu_dsa/chp2/Vector.h +++ b/thu_dsa/chp2/Vector.h @@ -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); diff --git a/thu_dsa/chp8/B-tree.md b/thu_dsa/chp8/B-tree.md index 34b585e..17187f9 100644 --- a/thu_dsa/chp8/B-tree.md +++ b/thu_dsa/chp8/B-tree.md @@ -20,3 +20,5 @@ m阶B树,即m路平衡搜索树。分支数上界为m,下界为[m/2]取上 ## B树的插入 B树高度增加的唯一情况,与B树根节点可以只拥有两个分支,之间的关系 + +由于只有上溢才能导致B树高度增加,B树的内部结点不可能有指向`nullptr`的指针 diff --git a/thu_dsa/chp8/BTNode.h b/thu_dsa/chp8/BTNode.h new file mode 100644 index 0000000..519e96f --- /dev/null +++ b/thu_dsa/chp8/BTNode.h @@ -0,0 +1,23 @@ +#ifndef BTNODE_H_ +#define BTNODE_H_ + +#include "../chp2/Vector.h" + +#define BTNodePosi(T) BTNode* + +template +class BTNode{ +public: + Vector keys; + Vector 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 diff --git a/thu_dsa/chp8/BTree.h b/thu_dsa/chp8/BTree.h new file mode 100644 index 0000000..ac15069 --- /dev/null +++ b/thu_dsa/chp8/BTree.h @@ -0,0 +1,194 @@ +#ifndef BTREE_H_ +#define BTREE_H_ + +#include "BTNode.h" + +template +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(); } + BTree(int order) : __order{ order } { __root = new BTNode(); } + + //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 +void BTree::solveOverflow(BTNodePosi(T) x){ + while (x->keys.getSize() == __order) { + int mid = __order / 2; + BTNodePosi(T) newLeaf = new BTNode(); + 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(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 +void BTree::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 +BTNodePosi(T) BTree::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 +bool BTree::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 +bool BTree::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 diff --git a/thu_dsa/chp8/test_BTree.cpp b/thu_dsa/chp8/test_BTree.cpp new file mode 100644 index 0000000..807fd34 --- /dev/null +++ b/thu_dsa/chp8/test_BTree.cpp @@ -0,0 +1,210 @@ +#include "BTree.h" +#include +#include + +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 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 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); +}