//==========================================================================
//
// dfs.h
//
//==========================================================================
// $Id: dfs.h,v 1.22 2000/03/07 07:41:34 raitner Exp $
#ifndef GTL_DFS_H
#define GTL_DFS_H
#include <GTL/GTL.h>
#include <GTL/algorithm.h>
__GTL_BEGIN_NAMESPACE
/**
* @short Depth-First-Search (DFS) algorithm
*
* Encapsulates the DFS-algoritm together with all the data produced by a
* run of DFS.
* <p>
* Since there exits so much different things which one might want to
* calculate during a DFS this class provides basically two different
* customization features. First it is possible to take influence on
* the behaviour of this algortihm by changing some of the
* following options:
*
* <ul>
* <li> @ref dfs#start_node
* (default: an arbitrary node will be chosen)
* <li> @ref dfs#scan_whole_graph states whether BFS will be
* continued in the unused part of the graph, if not all
* nodes were touched at the end of DFS started at the start-node.
* (default: disabled)
* <li> @ref dfs#calc_comp_num toggle storing of completion-numbers
* for each node, i.e. a numbering which reflects the order in which
* nodes were finished. (default: disabled)
* <li> @ref dfs#store_preds toggle storing the predecessor of each
* node, i.e. the father in DFS-tree. (default: disabled)
* <li> @ref dfs#store non_tree_edges toggle storing of all non_tree_edges
* (tree_edges are always stored) in a list and thus enable or disable
* iteration through all non_tree_edges.
* (default: disabled)
* </ul>
*
* <p>
* But the trouble with most DFS-algorithm is that one always wants to
* add a little bit of code somewhere in the algorithm. And then there are
* only two ways to get this done. The more efficient one (in terms of
* runtime) is to implement the DFS anew and add the new code where
* necessary. The other way (which is more efficient in terms of
* code-writing) is to take the algorithm as provided and run through the list
* of nodes it returns (resulting in an extra factor of 2)
*
* <p>
* Our DFS-algoritm class provides a new method to add small pieces of
* code to the algorithm: Handler. These are virtual functions called at
* certain important states of the algorithm (e.g. before a new recursive
* call). So the only thing to do is to derive your extended DFS from this
* class and to override the handlers where needed. In detail there are the
* following handler supported (have a look at the source code for details):
*
* <ul>
* <li> @ref dfs#init_handler
* <li> @ref dfs#end_handler
* <li> @ref dfs#entry_handler
* <li> @ref dfs#leave_handler
* <li> @ref dfs#before_recursive_call_handler
* <li> @ref dfs#after_recursive_call_handler
* <li> @ref dfs#old_adj_node_handler
* <li> @ref dfs#new_start_handler
* </ul>
*
* <p>
* Please note: We do <em>not</em> claim that this set of handler
* is sufficient in any way. So if you believe that some new handler is
* needed urgently please let us know.
*
* <p>
* There is a lot of information stored during DFS (e.g. nodes in
* dfs-order, list of non-tree edges). Some of it can be obtained directly
* by using the corresponding member-function (e.g. @ref dfs#dfs_num),
* but all information that can be thought of as a list (e.g. nodes in
* dfs-order) can be accessed through iterators. In detail these are (of
* course depending on what options are chosen!):
*
* <ul>
* <li> @ref dfs#dfs_iterator
* <li> @ref dfs#tree_edges_iterator
* <li> @ref dfs#non_tree_edges_iterator
* <li> @ref dfs#roots_iterator
* </ul>
*/
class GTL_EXTERN dfs : public algorithm
{
public:
//---------------------------------------------------------------------
// algorithm -- Interface
//---------------------------------------------------------------------
/**
* Default constructor. Options default like described above.
*
* @see algorithm#algorithm
*/
dfs ();
/**
* Destructor.
*
* @see algorithm#~algorithm
*/
virtual ~dfs ();
/**
* Performs DFS.
*
* @param <code>G</code> graph.
* @return <code>algorithm::GTL_OK</code> on success,
* <code>algorithm::GTL_ERROR</code> otherwise.
* @see algorithm#run
*/
int run (graph& G);
/**
* Checks whether the preconditions for DFS are satisfied.
*
* @param <code>G</code> graph.
* @return <code>algorithm::GTL_OK</code> on success,
* <code>algorithm::GTL_ERROR</code> otherwise.
* @see algorithm#check
*/
virtual int check (graph& G);
/**
* Reset. Please note that options like scanning of whole graph
* are <em>not</em> reset, but the chosen start node will be set back.
* So if applying this algorithm more than once with the same start
* node, it has to be set explicitly before every run.
*
* @see algorithm#reset
*/
virtual void reset ();
//---------------------------------------------------------------------
// Parameters
//---------------------------------------------------------------------
/**
* Sets start-node for DFS. The default start-node is the invalid node
* <code>node()</code>, in this case an arbitrary node is chosen and
* stored, when DFS is run.
*
* @param <code>n</code> start-node.
*/
void start_node (const node& n)
{ start = n; }
/**
* Returns start-node for DFS.
*
* @return start-node.
*/
node start_node () const {return start;}
/**
* Enables or disables scanning of the whole graph. If enabled and the
* DFS started at the given start-node stops without having touched all
* nodes, it will be continued with the next unused node, and so on
* until all nodes were used. This makes sure that for every node
* dfs_number is defined.
*
* <p>On the other hand, if this feature is disabled,
* one will be able to check what nodes can be reached, when starting a
* DFS at the start-node, because for those not reached dfs-number will
* be 0.
*
* @param <code>set</code> if true enable scanning the whole graph.
* @see dfs#roots_begin
* @see dfs#roots_end
*/
void scan_whole_graph (bool set) {whole_graph = set;}
/**
* Returns true iff the whole graph will be scanned.
*
* @return true iff the whole graph will be scanned.
* @see dfs#roots_begin
* @see dfs#roots_end
*/
bool scan_whole_graph () const {return whole_graph;}
/**
* Enables or Disables the calculation of the completion number.
*
* @param <code>set</code> if true completion-numbers will be calculated.
* @see dfs#comp_num
*/
void calc_comp_num (bool set);
/**
* Returns true iff completion-numbers will be calculated.
*
* @return true iff completion-numbers will be calculated.
* @see dfs#comp_num
*/
bool calc_comp_num () const {return comp_number != 0;}
/**
* Enables or disables the storing of predecessors. If enabled for
* every node the predecessor in DFS will be stored.
*
* @param <code>set</code> if true predecessors will be stored.
* @see dfs#father
*/
void store_preds (bool set);
/**
* Returns true iff the storing of predecessors is enabled.
*
* @return true iff the storing of predecessors is enabled.
* @see dfs#father
*/
bool store_preds () const {return preds != 0;}
/**
* Enables the storing of back-edges. If enabled the list of
* non-tree-edges can
* be traversed in the order they occured using
* <code>non_tree_edges_iterator</code>
*
* @param <code>set</code> if true non_tree_edges will be stored.
* @see dfs#non_tree_edges_begin
* @see dfs#non_tree_edges_end
*/
void store_non_tree_edges (bool set);
/**
* Returns true iff the storing of non-tree-edges is enabled.
*
* @return true iff the storing of non-tree-edges is enabled.
* @see dfs#non_tree_edges_begin
* @see dfs#non_tree_edges_end
*/
bool store_non_tree_edges () const {return back_edges != 0;}
//---------------------------------------------------------------------
// Access
//----------------------------------------------------------------------
/**
* Checks whether node <code>n</code> was reached in last DFS.
*
* @param <code>n</code> node to be checked.
* @return true iff <code>n</code> was reached.
*/
bool reached (const node& n) const
{return dfs_number[n] != 0;}
/**
* DFS-Number of <code>n</code>. Please note that DFS-Number 0 means
* that this node wasn't reached.
*
* @param <code>n</code> node.
* @return DFS-Number of <code>n</code>.
*/
int dfs_num (const node& n) const
{return dfs_number[n];}
/**
* DFS-Number of <code>n</code>. Please note that DFS-Number 0 means
* that this node wasn't reached.
*
* @param <code>n</code> node.
* @return DFS-Number of <code>n</code>.
*/
int operator[] (const node& n) const
{return dfs_number[n];}
/**
* Returns completion-number of node <code>n</code>, if enabled in last
* run.
*
* @param <code>n</code> node.
* @return Completion-number of <code>n</code>.
* @see dfs#calc_comp_num
*/
int comp_num (const node& n) const
{assert (comp_number); return (*comp_number)[n];}
/**
* Returns father of node <code>n</code> in DFS-forest. If
* <code>n</code> is a root
* in the forest or wasn't reached the return
* value is <code>node()</code>.
*
* @param <code>n</code> node.
* @return Father of <code>n</code>.
* @see dfs#store_preds
*/
node father (const node& n) const
{assert (preds); return (*preds)[n];}
/**
* @internal
*/
typedef list<edge>::const_iterator tree_edges_iterator;
/**
* Iterate through all edges picked in last DFS. Please note that this
* edges not always form a tree. In case the graph is not (strongly)
* connected they form a forest.
*
* @return start for iteration through all edges followed in DFS.
*/
tree_edges_iterator tree_edges_begin () const
{return tree.begin();}
/**
* End-Iterator for iteration through all edges picked in last DFS.
*
* @return end for iteration through all edges followed in DFS.
*/
tree_edges_iterator tree_edges_end () const
{return tree.end();}
/**
* @internal
*/
typedef list<node>::const_iterator dfs_iterator;
/**
* Iterate through all (reached) nodes in DFS-order.
*
* @return start for iteration through all nodes in DFS-order.
*/
dfs_iterator begin () const
{return dfs_order.begin();}
/**
* End-Iterator for iteration through all (reached) nodes in DFS-order.
*
* @return end for iteration through all (reached) nodes
*/
dfs_iterator end () const
{return dfs_order.end();}
/**
* @internal
*/
typedef list<edge>::const_iterator non_tree_edges_iterator;
/**
* Iterate through all non_tree_edges (if enabled).
*
* @return start for iteration through all non-tree-edges.
* @see dfs#store_non_tree_edges
*/
non_tree_edges_iterator non_tree_edges_begin () const
{assert (back_edges); return back_edges->begin(); }
/**
* End-iterator for iteration through all non-tree-edges (if enabled).
*
* @return end for iteration through all non-tree-edges.
* @see dfs#store_non_tree_edges
*/
non_tree_edges_iterator non_tree_edges_end () const
{assert (back_edges); return back_edges->end(); }
/**
* @internal
*/
typedef list<dfs_iterator>::const_iterator roots_iterator;
/**
* Iterator pointing towards the first root in the DFS-forest.
* <em>Please note</em> that intstead of pointing directly towards the node
* (i.e. <code>*it</code> is of type node) the iterator points towards
* a dfs-iterator, which represents the root (i.e. <code>*it</code>
* is of type <code>dfs_iterator</code>).
*
* <p>Using this technique
* makes it possible not only to obtain all the roots in the forest,
* but also the whole trees associated with each one. This can be achieved
* because a <code>root_iterator</code> specifies the exact position of
* the root in the DFS-ordering and by definition of DFS all the descendents of
* the root, i.e. the whole tree, will come later in DFS, such that by
* incrementing the <code>dfs_iterator</code>, a
* <code>roots_iterator</code>
* points at, one can traverse the whole tree with this given root.
*
* <p>Of course if the root isn't the last node in the DFS-forest on will
* also traverse all following trees, but since the first node of such
* a tree one will discover is its root, the successor of the
* <code>roots_iterator</code> can be used as end-iterator.
*
* @return start for iteration through all roots in DFS-forest.
* @see dfs#scan_whole_graph
*/
roots_iterator roots_begin () const
{return roots.begin();}
/**
* Iterator pointing to the end of all roots.
*
* @return end for iteration through all roots in DFS-forest.
* @see dfs#scan_whole_graph
*/
roots_iterator roots_end () const
{return roots.end();}
/**
* Number of nodes reached in last DFS.
*
* @return number of reached nodes.
* @see dfs#scan_whole_graph
*/
int number_of_reached_nodes () const
{return reached_nodes;}
//-----------------------------------------------------------------------
// Handler - for customization purposes
//-----------------------------------------------------------------------
/**
* Handler called before the start of DFS.
*
* @param <code>G</code> graph for which DFS was invoked.
*/
virtual void init_handler (graph& G) {}
/**
* Handler called at the end of DFS.
*
* @param <code>G</code> graph for which DFS was invoked.
*/
virtual void end_handler (graph& G) {}
/**
* Handler called when touching node <code>n</code>.
*
* @param <code>G</code> graph for which DFS was invoked.
* @param <code>n</code> actual node.
* @param <code>f</code> predecessor.
*/
virtual void entry_handler (graph& G, node& n, node& f) {}
/**
* Handler called after all the adjacent edges of <code>n</code> have been
* examined.
*
* @param <code>G</code> graph for which DFS was invoked.
* @param <code>n</code> actual node.
* @param <code>f</code> predecessor.
*/
virtual void leave_handler (graph& G, node& n, node& f) {}
/**
* Handler called when a unused node <code>n</code> connected to the
* actual node by <code>e</code> is found.
*
* @param <code>G</code> graph for which DFS was invoked.
* @param <code>e</code> edge connecting the actual node to the unused one.
* @param <code>n</code> unused node.
*/
virtual void before_recursive_call_handler (graph& G, edge& e, node& n) {}
/**
* Handler called after the algorithm return from the subtree starting
* at <code>n</code> connected to the actual node by <code>e</code>.
*
* @param <code>G</code> graph for which DFS was invoked.
* @param <code>e</code> edge connecting the actual node to the unused one.
* @param <code>n</code> unused node.
*/
virtual void after_recursive_call_handler (graph& G, edge& e, node& n) {}
/**
* Handler called when a already marked node <code>n</code> connected
* to the actual node by <code>e</code> is found during the search of all
* adjacent edges of the actual node
*
* @param <code>G</code> graph for which DFS was invoked.
* @param <code>e</code> edge connecting the actual node to the old one.
* @param <code>n</code> used node.
*/
virtual void old_adj_node_handler (graph& G, edge& e, node& n) {}
/**
* Called when DFS is started with start-node
* <code>n</code>. This is particularly useful when
* DFS was invoked with the <code>scan_whole_graph</code>
* option.
*
* @param <code>G</code> graph for which DFS was invoked.
* @param <code>n</code> start-node.
*/
virtual void new_start_handler (graph& G, node& n) { };
private:
/**
* @internal
*/
void dfs_sub (graph&, node&, node&);
protected:
//----------------------------------------------------------------------
// Data
//----------------------------------------------------------------------
/**
* @internal
*/
int act_dfs_num;
/**
* @internal
*/
int act_comp_num;
/**
* @internal
*/
list<edge> tree;
/**
* @internal
*/
list<node> dfs_order;
/**
* @internal
*/
node_map<int> dfs_number;
/**
* @internal
*/
int reached_nodes;
/**
* @internal
*/
edge_map<int>* used;
/**
* @internal
*/
list<dfs_iterator> roots;
//-----------------------------------------------------------------------
// Optional
//-----------------------------------------------------------------------
/**
* @internal
*/
node_map<int>* comp_number;
/**
* @internal
*/
node_map<node>* preds;
/**
* @internal
*/
list<edge>* back_edges;
/**
* @internal
*/
node start;
/**
* @internal
*/
bool whole_graph;
};
__GTL_END_NAMESPACE
#endif // GTL_DFS_H
//--------------------------------------------------------------------------
// end of file
//--------------------------------------------------------------------------
Documentation generated by raitner@hyperion on Tue Mar 7 10:13:50 CET 2000
|
Kdoc |