#ifndef TREE_H #define TREE_H ///////////////////////////////////////////////////////////////// // tree.h - generic m-ary tree // // ver 1.0 // // // // Language: Visual C++, ver 6.0, SP 5 // // Platform: Dell Dimension 8100, Win2000, SP 2 // // Application: CSE687 Project #1/#2 demonstration // // Author: Jim Fawcett, CST 2-187, Syracuse Univ. // // (315) 443-3948, jfawcett@twcny.rr.com // ///////////////////////////////////////////////////////////////// /* Class Operation =============== This module implements a template-based m-ary tree, that is, a tree in which each node may have any finite number of children. We make no attempt to keep the tree balanced. There are several classes defined for this module: - node class: ----------- Represents tree nodes and holds a generic type value. - criteria class: --------------- An interface class defining the criteria for searches in the tree. It is a functor, and its operator() returns a bool, indicating search success or failure for a given node. It accepts a tree node as an argument, so we can implement any type of search needed, based on the node's parameter T. - searchForValue class: --------------------- This class is derived from criteria and implements search for the first node to hold that value. - collectValues class: -------------------- It is also derived from criteria and implements a search for all nodes that hold a particular value. It saves them in a vector of node pointers and returns the vector on demand. - command class: -------------- Interface for a command object that the tree's walk function applies to every node in the tree, based on a depth-first search of the tree. - showNode class: --------------- Is derived from command and is the default operation supplied by the walk function. You can change the defalut to any class you derive from command by using the function tree::setCommand(command *pCmd) // - tree class: ----------- Provides the main functionality of this module. It builds a tree by accepting a node as its root. The user builds the tree by adding children, grandChildren, ... to the root node. The tree provides a walk function that applies some command object to each of its nodes in a depth-first walk. It also supplies find and findAll functions that use an object of the criteria class to determine which nodes satisfy the search criteria. Design Notes: ============= I had two objectives for the design of these classes: (1) to keep pointers out of the public interface, almost achieved. (2) to keep the tree and node general, not welded to this application. The use of command and criteria classes allows an application designer to derive from these interfaces supplying all application specific processing, so the tree and node classes do not have to. There is one significant constraint, imposed by this design. I had nodes hold their childred, rather than pointers to their children. This made memory management easier, but also means that two tree nodes cannot share the same child. That is not a problem for this appli- cation, but would require some redesign if the problem domain needs shared children. */ ///////////////////////////////////////////////////////////////////// // Build Process // ///////////////////////////////////////////////////////////////////// // Required Files: // // tree.h, tree.cpp // // // // Compiler Command: // // cl /GX /DTEST_TREE tree.cpp // // // ///////////////////////////////////////////////////////////////////// /* Maintenance History: -------------------- ver 1.0 : 20 Feb 02 - first release */ // #pragma warning(disable : 4786) #include #include /////////////////////////////////////////////////////////////// // node class declaration // template class node { public: node(); node(const node &nd); node(const T &t); ~node(); T& value(); int numChildren(); node& addChild(const node &nd); bool delChild(int i); node& child(int i); node child(int i) const; int& mark(); int next(); private: static int nodeCount; int _mark; // mark visited nodes T _value; // node's payload std::vector< node > _child; // child nodes }; /////////////////////////////////////////////////////////////// // static data member - counts the net number of objects // template int node::nodeCount = 0; //----< void constructor >------------------------------------- template node::node() : _mark(0) { ++nodeCount; } //----< copy constructor >------------------------------------- template node::node(const node &nd) : _child(nd._child), _mark(0), _value(nd._value) { ++nodeCount; } // //----< construct with value >--------------------------------- template node::node(const T &t) : _value(t), _mark(0) { ++nodeCount; } //----< destructor >------------------------------------------- template node::~node() { if(--nodeCount <= 0) cout << "\n deleted all nodes\n\n"; } //----< provide read/write access to node's value >------------ template T& node::value() { return _value; } //----< number of child nodes >-------------------------------- template int node::numChildren() { return _child.size(); } //----< add a child to this node >----------------------------- template node& node::addChild(const node &nd) { _child.push_back(nd); return *this; } //----< delete ith child node >-------------------------------- template bool node::delChild(int i) { if(i<0 || i>=_child.size()) return false; vector< node >::iterator it = _child.begin(); it += i; it = _child.erase(it); return true; } //----< return reference to ith child node >------------------- template node& node::child(int i) { if(i<0 || _child.size() <= i) throw "index out of domain in node::child(int index)"; return _child[i]; } // //----< return a copy of ith child node >---------------------- template node node::child(int i) const { if(i<0 || _child.size() <= i) throw "index out of domain in node::child(int index)"; return _child[i]; } //----< read or write child mark >----------------------------- template int& node::mark() { return _mark; } //----< mark the next child node >----------------------------- template int node::next() { if(_mark < _child.size()) return ++_mark; return _mark; } // /////////////////////////////////////////////////////////////// // search criteria functor class // template class criteria { public: virtual ~criteria() { } virtual bool operator()(node &nd) = 0; }; /////////////////////////////////////////////////////////////// // return reference to first node matching search value // template class searchForValue : public criteria { public: searchForValue(const T &val) : searchValue(val), pNode(0) { } bool operator()(node &nd) { if(nd.value() == searchValue) { pNode = &nd; return true; } return false; } node& node() { return *pNode; } // node with value void reset() { pNode = 0; } // prepare for next search private: int searchValue; // value to look for node *pNode; // pointer to first node holding value }; // /////////////////////////////////////////////////////////////// // collectValues collects all nodes matching search value // template class collectValues : public criteria { public: collectValues(const T &val) : searchValue(val) { } bool operator()(node &nd) { if(nd.value() == searchValue) { nodes.push_back(&nd); return true; } return false; } std::vector*> getNodes() { return nodes; } void reset() { nodes.clear(); } private: T searchValue; std::vector*> nodes; }; /////////////////////////////////////////////////////////////// // command object to be applied to each node during walk // template class command { public: virtual ~command() { } virtual void execute(node &nd)=0; }; /////////////////////////////////////////////////////////////// // default command displays node values // template class showNode : public command { public: virtual void execute(node &nd) { cout << "\n value of node is: " << nd.value(); } }; // /////////////////////////////////////////////////////////////// // tree class declaration // template class tree { public: tree(); ~tree(); void setRoot(node &nd); node& root(); void walk(node &nd); bool find(criteria &ok, node &nd); void findAll(criteria &collect, node &nd); void setCommand(command *pCommand); private: node *pRoot; command *pCommand; static showNode defaultCommand; }; /////////////////////////////////////////////////////////////// // static data member // template showNode tree::defaultCommand; //----< destructor >------------------------------------------- template tree::~tree() { cout << "\n deleting tree"; } //----< void constructor >------------------------------------- template tree::tree() : pRoot(0) { pCommand = &defaultCommand; } //----< give tree a root node >-------------------------------- template void tree::setRoot(node &nd) { pRoot = &nd; } //----< return reference to tree's root node >----------------- template node& tree::root() { return *pRoot; } // //----< depth first search >----------------------------------- template void tree::walk(node &nd) { pCommand->execute(nd); while(nd.mark() < nd.numChildren()) walk(nd.child(nd.mark()++)); nd.mark() = 0; cout << "\n--backing up"; } //----< find every node satisfying criteria >------------------ template void tree::findAll(criteria &collect, node &nd) { collect(nd); while(nd.mark() < nd.numChildren()) findAll(collect, nd.child(nd.mark()++)); nd.mark() = 0; } //----< find first node satisfying criteria >------------------ template bool tree::find(criteria &ok, node &nd) { if(ok(nd)) { nd.mark() = 0; return true; } while(nd.mark() < nd.numChildren()) { if(find(ok, nd.child(nd.mark()++))) { nd.mark() = 0; return true; } } nd.mark() = 0; return false; } //----< set a command to use with tree walk >------------------ template void setCommand(command *pCmd) { pCommand = pCmd; } #endif