create BST class, all tests passed.
This commit is contained in:
102
thu_dsa/chp7/BST.h
Normal file
102
thu_dsa/chp7/BST.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef BST_H_
|
||||
#define BST_H_
|
||||
|
||||
#include "../chp5/binTree.h"
|
||||
#include <stdexcept>
|
||||
|
||||
using std::runtime_error;
|
||||
|
||||
template <typename K, typename V>
|
||||
class entry {
|
||||
public:
|
||||
K key;
|
||||
V value;
|
||||
|
||||
//constructor
|
||||
entry(K k, V v) : key(k), value(v) {}
|
||||
|
||||
//overload operator
|
||||
bool operator==(entry<K, V> const e) { return key == e.key; }
|
||||
bool operator!=(entry<K, V> const e) { return key != e.key; }
|
||||
bool operator>(entry<K, V> const e) { return key > e.key; }
|
||||
bool operator<(entry<K, V> const e) { return key < e.key; }
|
||||
};
|
||||
|
||||
#define T entry<K, V>
|
||||
template <typename K, typename V>
|
||||
class BST: public BinTree<T>{
|
||||
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 <typename K, typename V>
|
||||
BinNodePosi(T)& BST<K, V>::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 <typename K, typename V>
|
||||
V BST<K, V>::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 <typename K, typename V>
|
||||
BinNodePosi(T)& BST<K, V>::search(K const &key){
|
||||
return searchIn(__root, key, __hot = nullptr);
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
BinNodePosi(T) BST<K, V>::insert(K const &key, V const &value){
|
||||
BinNodePosi(T) &x = search(key);
|
||||
if (x) return x;
|
||||
|
||||
x = new BinNode<T>(entry<K, V>(key, value), __hot);
|
||||
++__size;
|
||||
updateHeightAbove(__hot);
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
BinNodePosi(T) BST<K, V>::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
|
||||
23
thu_dsa/chp7/chp7.md
Normal file
23
thu_dsa/chp7/chp7.md
Normal file
@@ -0,0 +1,23 @@
|
||||
为什么会这样
|
||||
===========
|
||||
|
||||
## AVL
|
||||
|
||||
> 为什么AVL树是平衡树?
|
||||
|
||||
### AVL 插入
|
||||
+ 插入至多会导致$O(logn)$个结点失衡,全是当前结点的祖先。第一个失衡的结点,至少是新插入结点的祖父结点
|
||||
+ 经过一次旋转调整后,可以使第一个失衡的结点恢复平衡,同时它的祖先也全部恢复平衡,全树重新平衡
|
||||
+ 插入后有可能平衡性不变,但是高度发生改变
|
||||
|
||||
|
||||
### AVL 删除
|
||||
+ 删除至多只会导致一个结点失衡。这个结点可以是被删除结点的父结点
|
||||
+ 经过一次旋转调整后,当前局部会重新恢复平衡,但是其高度可能发生变化,也可能不变
|
||||
+ 因此之后的祖先结点也可能接着发生失衡。并且这种失衡至多会发生$O(logn)$次。因此至多需要$O(logn)$次调整
|
||||
+ 删除后有可能某一子树平衡性不变,但是高度降低
|
||||
+ 必须完全遍历至根节点,没有中途退出循环的途径。因为无法确定上层祖先是否会失衡。
|
||||
|
||||
## 3+4重构
|
||||
+ 可以证明,经过3+4重构后得到的子树,仍然是满足AVL平衡条件
|
||||
+ 对应了之前的单旋转和双旋转所有的情况
|
||||
97
thu_dsa/chp7/test_BST.cpp
Normal file
97
thu_dsa/chp7/test_BST.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "BST.h"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
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<int, string> 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<int, string>
|
||||
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<int, string> 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<int, string>
|
||||
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<int, string> 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]);
|
||||
}
|
||||
Reference in New Issue
Block a user