From 62761362a454e8cd48c148059f32c6fddda92da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Su=C3=A1rez?= Date: Tue, 9 Sep 2014 20:00:50 +0200 Subject: [PATCH 1/2] AVL tree added, but there's still a bug in the erasure method --- Makefile | 6 +- README.md | 20 +-- include/avl.h | 230 +++++++++++++++++++++++++++++++++++ include/binary_search_tree.h | 2 +- src/avl_demo.cpp | 59 +++++++++ 5 files changed, 305 insertions(+), 12 deletions(-) create mode 100644 include/avl.h create mode 100644 src/avl_demo.cpp diff --git a/Makefile b/Makefile index ed8cccc0..73ca5f8a 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CC=gcc CPP=g++ AR=ar RANLIB=ranlib -CFLAGS= -g -Wall -Wno-unused-function -std=gnu++0x +CFLAGS= -g -Wall -Wno-unused-function -std=c++11 SRCDIR = ./src INCLUDEDIR = -I./include -I. DEPS = @@ -70,7 +70,8 @@ PROGRAMS = m_based_demo \ 8queue_demo \ palindrome_demo \ suffix_array_demo \ - suffix_tree_demo + suffix_tree_demo \ + avl_demo all: $(PROGRAMS) @@ -79,3 +80,4 @@ all: $(PROGRAMS) clean: rm -rf $(PROGRAMS) *.dSYM + diff --git a/README.md b/README.md index e907418f..9f2ddb6f 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ Queue Stack Binary Heap - Fibonacci Heap + Fibonacci Heap Priority Queue (list based) - Bubble sort - Selection sort + Bubble sort + Selection sort Insertion sort Radix sort Quick sort @@ -51,13 +51,14 @@ Largest common sequence Binary search tree + AVL tree Dynamic order statistics Red-black tree Interval tree Prefix Tree(Trie) Suffix Tree B-Tree - Suffix Array + Suffix Array Hash by multiplication Hash table @@ -72,7 +73,7 @@ Base64 Graph data structure - Strongly Connected Components(SCC) + Strongly Connected Components(SCC) Prim's minimum spanning tree Kruskal MST Directed/Undirected graph ops @@ -89,13 +90,14 @@ K-Means Knuth–Morris–Pratt algorithm Disjoint-Set - 8-Queue Problem - Palindrome + 8-Queue Problem + Palindrome ####贡献者 ( Contributors ) : - Samana : for heavy work of MSVC compatability + Samana: for heavy work of MSVC compatability wycg1984: for K-Means xmuliang: for HeapSort, Kruskal MST wyh267: for base64, LRU, bubble sort, selection sort ZhangYou0122: Push-Relabel algorithm, Suffix Tree - UsingtcNower: Suffix Array + UsingtcNower: Suffix Array + afernandez90: AVL trees diff --git a/include/avl.h b/include/avl.h new file mode 100644 index 00000000..9dc6d621 --- /dev/null +++ b/include/avl.h @@ -0,0 +1,230 @@ +/******************************************************************************* + * ALGORITHM IMPLEMENTAIONS + * + * /\ | _ _ ._ o _|_ |_ ._ _ _ + * /--\ | (_| (_) | | |_ | | | | | _> + * _| + * + * Adelson-Velskii and Landis' (AVL) tree + * + * Features, being N the number of elements in the tree: + * 1. Guaranteed search time is O(log(N)). + * 2. Dynamically updated/balanced tree structure O(N) storage. + * 3. Exportable to GraphViz format for easy visualization and verification + * + * http://en.wikipedia.org/wiki/AVL_tree + * + ******************************************************************************/ + +#ifndef __AVL_H__ +#define __AVL_H__ + +#include +#include +#include + +namespace alg { + +template +class AVL { + + public: + + AVL() : tree(0), numNodes(0) {} + + T root () const { return tree->value; } + unsigned height() const { return Node::getHeight(tree); } + unsigned size() const { return numNodes; } + bool isEmpty() const { return numNodes == 0; } + + bool contains(const T &x) const { + if (!isEmpty()) { + return tree->contains(x); + } else return false; + } + + void insert(const T &x) { + if (isEmpty()) tree = new Node(x); + else tree = tree->insert(x); + numNodes++; + } + + void erase(const T &x) { + if (!isEmpty()) { + tree = tree->erase(x); + numNodes--; + } + } + + void toGraphViz(std::ostream &stream, std::string name) const { + if (!isEmpty()) { + stream << "digraph " << name << " {" << std::endl; + tree->toGraphViz(stream); + stream << "}" << std::endl; + } + } + + private: + + struct Node { + Node *left, *right; + T value; + unsigned height; + + Node(const T &x) : left(0), right(0), value(x), height(1) {} + + bool contains(const T &x) const { + if (value == x) return true; + else if (x < value && left != 0) return left->contains(x); + else if (right != 0) return right->contains(x); + else return false; + } + + Node *insert(const T &x) { + if (x <= value) { + if (left == 0) left = new Node(x); + else left = left->insert(x); + } + else { + if (right == 0) right = new Node(x); + else right = right->insert(x); + } + + return update(); + } + + Node *erase(const T &x) { + if (value == x) { + if (left == 0 && right == 0) { + delete this; + return 0; + } else if (left == 0) { + *this = *right; + delete right; + } else if (right == 0) { + *this = *left; + delete left; + } else { + // Tracing path to rightmost leaf of the left subtree + std::stack trace; + + Node *current = left; + while (current->right != 0) { + trace.push(current); + current = current->right; + } + + value = current->value; + Node *lsubtree = current->left; + delete current; + + if (trace.empty()) trace.push(left); + + trace.top()->right = lsubtree; + + do { + trace.top()->update(); + trace.pop(); + } while (!trace.empty()); + } + return update(); + } + else if (x < value) { + if (left != 0) { + left = left->erase(x); + return update(); + } else return this; + } + else { + if (right != 0) { + right = right->erase(x); + return update(); + } else return this; + } + } + + Node *update() { + updateHeight(); + + if (getBF(this) >= 2) { + if (getBF(left) <= -1) LR(); + return LL(); + } else if (getBF(this) <= -2) { + if (getBF(right) >= 1) RL(); + return RR(); + } else return this; + } + + void updateHeight() { height = std::max(getHeight(left), getHeight(right)) + 1; } + + void LR() { + Node *lrcopy = left->right; + left->right = lrcopy->left; + lrcopy->left = left; + left = lrcopy; + left->left->updateHeight(); + left->updateHeight(); + updateHeight(); + } + + void RL() { + Node *rlcopy = right->left; + right->left = rlcopy->right; + rlcopy->right = right; + right = rlcopy; + right->right->updateHeight(); + right->updateHeight(); + updateHeight(); + } + + Node *LL() { + Node *lcopy = left; + left = left->right; + lcopy->right = this; + lcopy->left->updateHeight(); + lcopy->right->updateHeight(); + lcopy->updateHeight(); + return lcopy; + } + + Node *RR() { + Node *rcopy = right; + right = right->left; + rcopy->left = this; + rcopy->left->updateHeight(); + rcopy->right->updateHeight(); + rcopy->updateHeight(); + return rcopy; + } + + static int getBF(const Node *t) { + return getHeight(t->left) - getHeight(t->right); + } + + static int getHeight(const Node *t) { + return t == 0 ? 0 : t->height; + } + + void toGraphViz(std::ostream &stream) const { + stream << value << ";" << std::endl; + if (left != 0) { + stream << left->value << ";" << std::endl; + stream << value << "->" << left->value << ";" << std::endl; + left->toGraphViz(stream); + } + if (right != 0) { + stream << right->value << ";" << std::endl; + stream << value << "->" << right->value << ";" << std::endl; + right->toGraphViz(stream); + } + } + }; + + Node *tree; + unsigned numNodes; +}; + +} // namespace alg + +#endif // _ALG_AVL_HPP + diff --git a/include/binary_search_tree.h b/include/binary_search_tree.h index 21ff297a..5066c771 100644 --- a/include/binary_search_tree.h +++ b/include/binary_search_tree.h @@ -8,7 +8,7 @@ * BINARY SEARCH TREE * * Features: - * 1. Expected search time is O(nlogn). + * 1. Expected search time is O(log(n)), with worst case O(n). * 2. Data should be !!!SHUFFLED!!! first before tree creation. * 3. First initialize the value of the root (pointer to the * structure treeNode) with NULL. eg: diff --git a/src/avl_demo.cpp b/src/avl_demo.cpp new file mode 100644 index 00000000..9eb66173 --- /dev/null +++ b/src/avl_demo.cpp @@ -0,0 +1,59 @@ +#include +#include "avl.h" + +using namespace std; +using namespace alg; + +const unsigned N = 4096*32; +const unsigned N_ELEMS_TO_REMOVE = 4096*8; + +template +void printTreeStatus(const AVL &t) { + cout << "----------------------------------------" << endl; + if (t.isEmpty()) cout << "The tree is empty" << endl; + else { + cout << "Tree root is: " << t.root() << endl; + cout << "Tree height is: " << t.height() << endl; + cout << "Tree contains " << t.size() << " elements" << endl; + } + cout << "----------------------------------------" << endl; +} + +int main() +{ + int values[N]; + + AVL avl; + + cout << "Populating the tree with " << N << " random values... "; + for (unsigned i = 0; i < N; ++i) { + values[i] = rand(); + avl.insert(values[i]); + } + cout << "Done" << endl; + + printTreeStatus(avl); + + for (unsigned i = 0; i < N; ++i) { + unsigned idx = rand() % N; + if (!avl.contains(values[idx])) + cout << "ERROR: Value " << values[idx] << " was inserted and not found!" << endl; + } + + cout << "Now removing " << N_ELEMS_TO_REMOVE << " random elements for the tree... "; + for (unsigned i = 0; i < N_ELEMS_TO_REMOVE; ++i) { + unsigned idx = rand() % N; + avl.erase(values[idx]); + } + cout << "Done" << endl; + + printTreeStatus(avl); + + cout << "Do you want to see the GraphViz representation of the Tree (Y/n)? "; + char usrInput; + cin >> usrInput; + if (usrInput == 'Y' || usrInput == 'y') avl.toGraphViz(cout, "AVL"); + + return 0; +} + From 3171767d370f75b099550c7d10590987d6e5b074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Su=C3=A1rez?= Date: Wed, 10 Sep 2014 18:39:42 +0200 Subject: [PATCH 2/2] bug fixed, AVL trees now working properly --- include/avl.h | 40 ++++++++++++++++++++++++---------------- src/avl_demo.cpp | 21 ++++++++++++++------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/include/avl.h b/include/avl.h index 9dc6d621..e9ffe90c 100644 --- a/include/avl.h +++ b/include/avl.h @@ -51,8 +51,9 @@ class AVL { void erase(const T &x) { if (!isEmpty()) { - tree = tree->erase(x); - numNodes--; + bool found = false; + tree = tree->erase(x, found); + if (found) numNodes--; } } @@ -64,7 +65,7 @@ class AVL { } } - private: + public: struct Node { Node *left, *right; @@ -93,51 +94,58 @@ class AVL { return update(); } - Node *erase(const T &x) { + Node *erase(const T &x, bool &found) { if (value == x) { + found = true; if (left == 0 && right == 0) { delete this; return 0; } else if (left == 0) { + Node *aux = right; *this = *right; - delete right; + delete aux; } else if (right == 0) { + Node *aux = left; *this = *left; - delete left; + delete aux; } else { // Tracing path to rightmost leaf of the left subtree std::stack trace; Node *current = left; - while (current->right != 0) { + while (current != 0) { trace.push(current); current = current->right; } + current = trace.top(); value = current->value; Node *lsubtree = current->left; delete current; + trace.pop(); - if (trace.empty()) trace.push(left); - - trace.top()->right = lsubtree; - - do { - trace.top()->update(); + if (trace.empty()) { left = lsubtree; } + else { + trace.top()->right = lsubtree; trace.pop(); - } while (!trace.empty()); + while (!trace.empty()) { + current = trace.top(); + current->right = current->right->update(); + trace.pop(); + } + } } return update(); } else if (x < value) { if (left != 0) { - left = left->erase(x); + left = left->erase(x, found); return update(); } else return this; } else { if (right != 0) { - right = right->erase(x); + right = right->erase(x, found); return update(); } else return this; } diff --git a/src/avl_demo.cpp b/src/avl_demo.cpp index 9eb66173..444185f0 100644 --- a/src/avl_demo.cpp +++ b/src/avl_demo.cpp @@ -5,7 +5,7 @@ using namespace std; using namespace alg; const unsigned N = 4096*32; -const unsigned N_ELEMS_TO_REMOVE = 4096*8; +const unsigned N_ELEMS_TO_REMOVE = N-128; // Must be between 0 and N-1 template void printTreeStatus(const AVL &t) { @@ -40,20 +40,27 @@ int main() cout << "ERROR: Value " << values[idx] << " was inserted and not found!" << endl; } - cout << "Now removing " << N_ELEMS_TO_REMOVE << " random elements for the tree... "; + cout << "Now removing a random element from the tree... "; + unsigned idx = rand() % N; + avl.erase(values[idx]); + cout << "Done" << endl; + + printTreeStatus(avl); + + cout << "Now removing the root of the tree " << N_ELEMS_TO_REMOVE << " times... "; for (unsigned i = 0; i < N_ELEMS_TO_REMOVE; ++i) { - unsigned idx = rand() % N; - avl.erase(values[idx]); + avl.erase(avl.root()); } cout << "Done" << endl; printTreeStatus(avl); - cout << "Do you want to see the GraphViz representation of the Tree (Y/n)? "; + // Outputting to cerr so the output can be redirected with ./avl_demo 2> .gvz + cout << "Do you want to output the GraphViz representation of the tree to the cerr stream (Y/n)? "; char usrInput; cin >> usrInput; - if (usrInput == 'Y' || usrInput == 'y') avl.toGraphViz(cout, "AVL"); - + if (usrInput == 'Y' || usrInput == 'y') avl.toGraphViz(cerr, "AVL"); + return 0; }