diff --git a/thu_dsa/chp7/BST.h b/thu_dsa/chp7/BST.h new file mode 100644 index 0000000..1770571 --- /dev/null +++ b/thu_dsa/chp7/BST.h @@ -0,0 +1,102 @@ +#ifndef BST_H_ +#define BST_H_ + +#include "../chp5/binTree.h" +#include + +using std::runtime_error; + +template +class entry { +public: + K key; + V value; + + //constructor + entry(K k, V v) : key(k), value(v) {} + + //overload operator + bool operator==(entry const e) { return key == e.key; } + bool operator!=(entry const e) { return key != e.key; } + bool operator>(entry const e) { return key > e.key; } + bool operator<(entry const e) { return key < e.key; } +}; + +#define T entry +template +class BST: public BinTree{ +protected: + BinNodePosi(T) __hot; + BinNodePosi(T)& searchIn(BinNodePosi(T)& x, K const &key, BinNodePosi(T) &hot); + BinNodePosi(T) connect34(BinNodePosi(T), BinNodePosi(T), BinNodePosi(T), + BinNodePosi(T), BinNodePosi(T), BinNodePosi(T), BinNodePosi(T)); + BinNodePosi(T) rotateAt(BinNodePosi(T) x); + + +public: + //call by key, always assume that key exists, read-only + V operator[](K const &key); + + virtual BinNodePosi(T)& search(K const &key); + virtual BinNodePosi(T) insert(K const &key, V const &value); + virtual BinNodePosi(T) remove(K const &key); +}; + +//protected methods +template +BinNodePosi(T)& BST::searchIn(BinNodePosi(T)& x, K const &key, BinNodePosi(T) &hot){ + if (!x || x->data.key == key) return x; + hot = x; + return key < x->data.key ? searchIn(x->leftChild, key, hot) : searchIn(x->rightChild, key, hot); +} + +//public interfaces + +template +V BST::operator[](K const &key){ + BinNodePosi(T) x = search(key); + if (!x) throw runtime_error("Fatal Error: key doesn't exist."); + return x->data.value; +} + +template +BinNodePosi(T)& BST::search(K const &key){ + return searchIn(__root, key, __hot = nullptr); +} + +template +BinNodePosi(T) BST::insert(K const &key, V const &value){ + BinNodePosi(T) &x = search(key); + if (x) return x; + + x = new BinNode(entry(key, value), __hot); + ++__size; + updateHeightAbove(__hot); + return x; +} + +template +BinNodePosi(T) BST::remove(K const &key){ + BinNodePosi(T)& x = search(key); + BinNodePosi(T) succ; + if (!x) return nullptr; + + if (!x->leftChild) succ = x = x->rightChild; + else if (!x->rightChild) succ = x = x->leftChild; + else{ + succ = x->succ(); + x->data = succ->data; + + succ->parent == x ? succ->parent->rightChild : succ->parent->leftChild = succ->rightChild; + __hot = succ; + succ = __hot->rightChild; + } + if (succ) succ->parent = __hot; + --__size; + updateHeightAbove(__hot); + return succ; +} + +#undef T + +#endif diff --git a/thu_dsa/chp7/chp7.md b/thu_dsa/chp7/chp7.md new file mode 100644 index 0000000..10be14b --- /dev/null +++ b/thu_dsa/chp7/chp7.md @@ -0,0 +1,23 @@ +为什么会这样 +=========== + +## AVL + +> 为什么AVL树是平衡树? + +### AVL 插入 ++ 插入至多会导致$O(logn)$个结点失衡,全是当前结点的祖先。第一个失衡的结点,至少是新插入结点的祖父结点 ++ 经过一次旋转调整后,可以使第一个失衡的结点恢复平衡,同时它的祖先也全部恢复平衡,全树重新平衡 ++ 插入后有可能平衡性不变,但是高度发生改变 + + +### AVL 删除 ++ 删除至多只会导致一个结点失衡。这个结点可以是被删除结点的父结点 ++ 经过一次旋转调整后,当前局部会重新恢复平衡,但是其高度可能发生变化,也可能不变 ++ 因此之后的祖先结点也可能接着发生失衡。并且这种失衡至多会发生$O(logn)$次。因此至多需要$O(logn)$次调整 ++ 删除后有可能某一子树平衡性不变,但是高度降低 ++ 必须完全遍历至根节点,没有中途退出循环的途径。因为无法确定上层祖先是否会失衡。 + +## 3+4重构 ++ 可以证明,经过3+4重构后得到的子树,仍然是满足AVL平衡条件 ++ 对应了之前的单旋转和双旋转所有的情况 diff --git a/thu_dsa/chp7/test_BST.cpp b/thu_dsa/chp7/test_BST.cpp new file mode 100644 index 0000000..1002105 --- /dev/null +++ b/thu_dsa/chp7/test_BST.cpp @@ -0,0 +1,97 @@ +#include "BST.h" +#include +#include +#include + +using std::cout; +using std::endl; +using std::string; + +void test_insert_and_search(); +void test_remove(); +void test_index(); + +int main(){ + cout << "Running test." << endl; + + test_insert_and_search(); + test_remove(); + test_index(); + + cout << "All tests passed." << endl; + system("pause"); + return 0; +} + +void test_insert_and_search(){ + BST bstTree; + int keys[] = { 5, 8, 2, 7, 6, 4, 9, 1, 3 }; + string values[] = { "five", "eight", "two", "seven", "six", "four", "nine", "one", "three" }; + + for (int ix = 0; ix != 9; ++ix) + bstTree.insert(keys[ix], values[ix]); + assert(bstTree.size() == 9); + + //test duplicates + for (int ix = 0; ix != 9; ++ix) + bstTree.insert(keys[ix], values[8 - ix]); + assert(bstTree.size() == 9); + + //test search + for(int ix = 0; ix != 9; ++ix) + assert(bstTree.search(keys[ix])->data.value == values[ix]); + + //test BST characteristics +#define T entry + BinNodePosi(T) x = bstTree.search(1); + for (int ix = 1; x; ++ix, x = x->succ()) + assert(x->data.key == ix); +#undef T +} + +void test_remove(){ + BST bstTree; + int keys[] = { 5, 8, 2, 7, 6, 4, 9, 1, 3 }; + string values[] = { "five", "eight", "two", "seven", "six", "four", "nine", "one", "three" }; + for (int ix = 0; ix != 9; ++ix) + bstTree.insert(keys[ix], values[ix]); + + assert(bstTree.root()->data.key == 5); + //remove leaf + assert(bstTree.remove(6) == nullptr);//no succ + assert(bstTree.size() == 8); + assert(bstTree.search(6) == nullptr); + + //remove intermediate node + assert(bstTree.remove(4)->data.value == "three"); + assert(bstTree.size() == 7); + assert(bstTree.search(4) == nullptr); + assert(bstTree.search(3)->parent->data.key == 2); + assert(bstTree.search(2)->rightChild->data.key == 3); + + //remove root node + assert(bstTree.remove(5) == nullptr); + assert(bstTree.size() == 6); + assert(bstTree.root()->data.key == 7); + assert(bstTree.root()->leftChild->data.key == 2 && bstTree.root()->rightChild->data.key == 8); + + //test BST topology +#define T entry + BinNodePosi(T) x = bstTree.search(1); + int remainedKeys[] = { 1, 2, 3, 7, 8, 9 }; + for (int ix = 0; x; ++ix, x = x->succ()) { + assert(x->data.key == remainedKeys[ix]); + } +#undef T +} + +void test_index(){ + BST bstTree; + int keys[] = { 5, 8, 2, 7, 6, 4, 9, 1, 3 }; + string values[] = { "five", "eight", "two", "seven", "six", "four", "nine", "one", "three" }; + for (int ix = 0; ix != 9; ++ix) + bstTree.insert(keys[ix], values[ix]); + + for (int ix = 0; ix != 9; ++ix) + assert(bstTree[keys[ix]] == values[ix]); +}