// --------------------------------------------------------------------------- // BST.h // // A BST class that you can reuse by decorating. It does insertion, // lookup, rotations, and splays. There is no remove operation, // but you can call clear() to empty it out. // // The best way to use this class to make splay trees, AVL trees, // Red-black trees and so on is by delegation, though in some cases // you may be able to get away with subclassing. But as you know, // delegation (composition) is generally preferable to subclassing. // --------------------------------------------------------------------------- #ifndef _BST_H #define _BST_H #include <stdexcept> using namespace std; // A BST object contins only a pointer to its root node and its // (cached) size. It is assumed that whatever type is being // contained in the tree has acceptable "<" and "==" operations. template<class T> class BST { // Inner class for binary tree nodes. Each node contains some data, and // references to its left child, right child, and parent. private: class Node { public: T data; Node* left; Node* right; Node* parent; BST* tree; // Construct a new (leaf) node Node(T data, Node* parent, BST* tree): data(data), left(0), right(0), parent(parent), tree(tree) { } // Destroy a node and its subtrees ~Node() { delete left; delete right; } // Return the data item stored in this node. T getData() { return data; } // Replace the contents of this node with new data, returning // what used to be there. T setData(T data) { T oldData = this->data; this->data = data; return oldData; } // These methods help to ensure consistency!! void makeRoot() {tree->root = this; parent = 0;} void setLeft(Node* n) {left = n; if (n != 0) n->parent = this;} void setRight(Node* n) {right = n; if (n != 0) n->parent = this;} // Rotations. Note I actually check whether the rotation can legally be // performed. It's slower, I guess, but it's good bulletproofing. In // production code, the check might be eliminated since the BST would be // totally buried. void rotateLeft() { if (right == 0) return; Node* oldRight = right; setRight(oldRight->left); if (parent == 0) oldRight->makeRoot(); else if (parent->left == this) parent->setLeft(oldRight); else parent->setRight(oldRight); oldRight->setLeft(this); } void rotateRight() { if (left == 0) return; Node* oldLeft = left; setLeft(oldLeft->right); if (parent == 0) oldLeft->makeRoot(); else if (parent->left == this) parent->setLeft(oldLeft); else parent->setRight(oldLeft); oldLeft->setRight(this); } // Splays this node. The implementation isn't so bad once you // realize it uses the primitive rotation methods. Of course // when you call the rotate methods you do take a bit // of a performance hit. Production code would do each // case inline. NOTE: An interesting educational exercise // would be to comment out the "while" line and its // ending brace so you can watch each step of the splay // if you have a good graphical tester. void splay() { while (this != tree->root) { if (parent->parent == 0) { if (this == parent->right) parent->rotateLeft(); else parent->rotateRight(); } else if (this == parent->right && parent == parent->parent->right) { parent->parent->rotateLeft(); parent->rotateLeft(); } else if (this == parent->left && parent == parent->parent->left) { parent->parent->rotateRight(); parent->rotateRight(); } else if (this == parent->right && parent == parent->parent->left) { parent->rotateLeft(); parent->rotateRight(); } else if (this == parent->left && parent == parent->parent->right) { parent->rotateRight(); parent->rotateLeft(); } } } // A neat recursive method to delete all the nodes. void destroySubtree() { if (left != 0) left->destroySubtree(); if (right != 0) right->destroySubtree(); delete this; } }; // The tree itself is just a pointer to its root. private: Node<T>* root; int size; // Nested classes can't automatically see private fields in C++ friend class Visitor; friend class Node; // Constructors and Destructors public: // Constructs a new EMPTY binary search tree. The tree is represented // with a pointer to the root and its size. BST(): root(0), size(0) { } // Deletes all the nodes. ~BST() { clear(); } // Behavior public: // Returns the number of items in the list. int getSize() { return size; } // Returns whether the list is empty. bool isEmpty() { return size == 0; } // Returns whether the given data is in some node in the tree. // We defer the work to another method which returns the node // containing the item if it exists and null otherwise. bool contains(T data) { return nodeContaining(data) != 0; } // Adds a single data item to the tree. If there is already an // item in the tree that compares equal to the item being inserted, // it is "overwritten" by the new item and returned. void add(T data) { if (root == 0) { root = new Node(data, 0, this); size++; } Node* n = root; while (true) { if (data == n->data) return; else if (data < n->data) { if (n->left != 0) n = n->left; else {size++; n->left = new Node(data, n, this); return;} } else { // data > n->data if (n->right != 0) n = n->right; else {size++; n->right = new Node(data, n, this); return;} } } } // Removes all the items in the tree. void clear() { delete root; size = 0; root = 0; } // A visitor for the binary search tree. It is a classic // example of the template method pattern. Calling go(t) will // do the Euler Tour, depth-first, left-to-right, so you'll hit // each node three times. To do traversals, simply make a // subclass of Vistor and implement any or all of the five // callbacks. class Visitor { protected: virtual void initialize() {} virtual void onLeft(Node* node) {} virtual void onBelow(Node* node) {} virtual void onRight(Node* node) {} virtual T getResult() {return T();} public: T go(const BST& t) { initialize(); if (t.root != 0) {visitFrom(t.root);} return getResult(); } private: void visitFrom(Node* n) { onLeft(n); if (n->left != 0) {visitFrom(n->left);} onBelow(n); if (n->right != 0) {visitFrom(n->right);} onRight(n); } }; // Helper methods public: // A special helper method that returns the node containing // a given object. We make it package-visible instead of // protected since subclasses don't care about it, but testers // might! Node* nodeContaining(T data) { for (Node* n = root; n != 0;) { if (data == n->data) { return n; } else if (data < n->data) { n = n->left; } else { n = n->right; } } // not found return 0; } // Prohibit copying and assignment private: BST(BST&); BST& operator=(BST&); }; #endif