Please send questions to
st10@humboldt.edu .
/************************************************************/
/* ADAPTED FROM: */
/* http://www.cs.colorado.edu/~main/chapter12/table2.h */
/* */
/* Main and Savitch, "Data Structures and Other Objects */
/* using C++", 2nd edition, Addison-Wesley, Ch.12, p.587 */
/************************************************************/
//-----------------------------------------------------------
// File: graph.template
// Name: Michael Main, Walter Savitch
// (adapted by Sharon M. Tuttle)
// last modified: 4-27-05
//
// Template Class: graph<Item> (a graph where each vertex has
// a label of type Item; this label may be
// any of the C++ built-in types or any class
// with a default constructor and an assignment
// operator. The graph may not have multiple
// edges between two vertices.
//
// VALUE SEMANTICS for the graph<Item> template class:
// Assignments and the copy constructor may be used
// with graph<Item> objects.
//
// DYNAMIC MEMORY USAGE by the graph<Item> template class:
// If there is insufficient dynamic memory, then the
// following functions throw bad_alloc:
// constructor, the copy constructor, add_edge,
// add_vertex, and the assignment operator. [I THINK]
//-----------------------------------------------------------
#include "graph.h"
#include <iostream>
#include <cassert>
#include <algorithm> // for copy...
using namespace std;
/**********************************************************/
/* CONSTRUCTORS and DESTRUCTOR */
/**********************************************************/
// constructor
//
// postcondition: initializes a graph with no vertices
// and no edges.
//
template <typename Item>
graph<Item>::graph( )
{
total_vertices = 0;
total_edges = 0;
// arrays adj_to_vertex and vertices are not dynamic,
// in this case. If they were, I'd need to initialize
// them to the desired size here. But, since they are
// not dynamic, they were declared in the private part
// in graph.h to size MAXIMUM.
// BUT --- I do need to make all of the adj_to_vertex
// pointers NULL, to start;
for (int i=0; i < graph<Item>::MAXIMUM; i++)
{
adj_to_vertex[i] = NULL;
}
}
// copy constructor
//
template <typename Item>
graph<Item>::graph(const graph<Item>& source)
{
total_vertices = source.total_vertices;
total_edges = source.total_edges;
// copy over vertex labels
copy(source.vertices, source.vertices + total_vertices, vertices);
// copy over edge lists
node<Item> *tail_ptr; // needed for argument of list_copy
for (int i=0; i<MAXIMUM; i++)
{
list_copy(source.adj_to_vertex[i], adj_to_vertex[i],
tail_ptr);
tail_ptr = NULL;
}
}
template <typename Item>
graph<Item>::~graph( )
{
for (int i=0; i<total_vertices; i++)
{
list_clear(adj_to_vertex[i]);
}
}
/*******************************************************/
/* ACCESSORS and other constant member functions */
/* (observers) */
/*******************************************************/
// get_num_vertices
//
// postcondition: returns the number of vertices in
// the graph.
//
template <typename Item>
size_t graph<Item>::get_num_vertices( ) const
{
return total_vertices;
}
// get_num_edges
//
// postcondition: returns the number of edges in the
// graph.
//
template <typename Item>
size_t graph<Item>::get_num_edges( ) const
{
return total_edges;
}
// is_vertex
//
// postcondition: returns true if vert is the label of
// a vertex in this graph; returns false otherwise.
//
template <typename Item>
bool graph<Item>::is_vertex(Item vert) const
{
bool found = false;
// look through vertices, seeing if this label
// is there. Short-circuit (exit immediately)
// if vert is found...
for (int i=0; i < total_vertices; i++)
{
if (vertices[i] == vert)
{
return true;
}
}
// if reach here --- vert is NOT one of this graph's
// vertices.
return false;
}
// is_edge
//
// preconditions: is_vertex(source) == true,
// is_vertex(target) == true
// postconditions: returns true if (source, target) is
// an edge in this graph, and returns false otherwise.
//
template <typename Item>
bool graph<Item>::is_edge(Item source, Item target) const
{
assert (is_vertex(source) == true);
assert (is_vertex(target) == true);
size_t source_index = get_vert_index(source);
node<Item> *curr;
curr = adj_to_vertex[source_index];
// is target in source's list of adjacent vertices?
while ((curr != NULL) && (curr->get_data( ) != target))
{
curr = curr->get_next( );
}
if (curr == NULL)
{
// target was not in source's adjacency list
return false;
}
else
{
// target must have been in source's adjacency list
return true;
}
}
// get_neighbors
//
// precondition: is_vertex(vert) == true
// postcondition: returns a set containing all of the
// labels of vertices that are the target of an edge
// whose source is vert.
//
template <typename Item>
set<Item> graph<Item>::get_neighbors(Item vert) const
{
set<Item> neighbor_set;
size_t vert_index = get_vert_index(vert);
node<Item> *curr;
curr = adj_to_vertex[vert_index];
// add all adjacent vertices to neighbor_set
// for this vertex
while (curr != NULL)
{
neighbor_set.insert( curr->get_data( ) );
curr = curr->get_next( );
}
return neighbor_set;
}
// print_graph
//
// postcondition: prints the graph in the following
// form: first the set of vertices, then the edges
// between the vertices.
//
template <typename Item>
void graph<Item>::print_graph( ) const
{
node<Item> *curr;
Item curr_vertex;
size_t curr_vertex_index;
cout << "printing vertices and edges of graph: " << endl;
cout << "-----------------------------------------" << endl;
// print vertices...
cout << "V = {";
for (int i=0; i<total_vertices; i++)
{
cout << vertices[i] << " ";
}
cout << "} " << endl;
// now print edges...
cout << endl;
cout << "E = {";
for (int i=0; i<total_vertices; i++)
{
if (adj_to_vertex[i] != NULL)
{
curr_vertex = vertices[i];
curr = adj_to_vertex[i];
while (curr != NULL)
{
cout << "(" << curr_vertex << ",";
cout << (curr->get_data( )) << ") ";
curr = curr->get_next( );
}
// "offset" edges adjacent from each vertex
cout << endl << " ";
}
}
cout << "} " << endl;
}
//***************************************************/
/* MODIFIERS and other modifying member functions */
//***************************************************/
// add_vertex
//
// preconditions: get_num_vertices( ) < MAXIMUM,
// is_vertex(new_vert) == false
// postconditions: the number of vertices in the graph
// has been increased by adding vertex new_vert, which
// currently has no edges.
//
template <typename Item>
void graph<Item>::add_vertex(const Item& new_vert)
{
assert (get_num_vertices( ) < graph<Item>::MAXIMUM);
assert (is_vertex(new_vert) == false);
size_t new_vert_index = total_vertices;
total_vertices++;
// probably don't NEED to do this --- but, to play it
// safe, make SURE that this vertex CLEARLY has no
// adjacent vertices yet
adj_to_vertex[new_vert_index] = NULL;
// DO need to add this vertex to vertices array!
vertices[new_vert_index] = new_vert;
}
// add_edge
//
// preconditions: is_vertex(source) == true,
// is_vertex(target) == true
// postconditions: if there was not an edge from
// source to target before, then there is, now
// (and the number of edges has been increased).
// (If there was an edge from source to target
// before, then the graph is unchanged.)
//
template <typename Item>
void graph<Item>::add_edge(Item source, Item target)
{
size_t source_index, target_index;
// if this edge already exists, no action is needed.
if (is_edge(source, target))
{
return;
}
// IF reach here, however, this edge SHOULD be added
source_index = get_vert_index(source);
target_index = get_vert_index(target);
list_head_insert(adj_to_vertex[source_index], target);
list_head_insert(adj_to_vertex[target_index], source);
total_edges++;
}
// remove_edge
//
// preconditions: is_vertex(source) == true,
// is_vertex(target) == true
// postconditions: if there was an edge from source
// target before, then it has now been removed
// (and the numberof edges has been decreased).
// (If there had not been an edge from source to
// target before, then the graph is unchanged.)
//
template <typename Item>
void graph<Item>::remove_edge(Item source, Item target)
{
size_t source_index, target_index;
assert( is_vertex(source) == true );
assert( is_vertex(target) == true );
// if this is not currently an edge, no action is needed
if ( ! is_edge(source, target) )
{
return;
}
// IF reach here, though, this edge DOES exist in graph
total_edges--;
source_index = get_vert_index(source);
target_index = get_vert_index(target);
node<Item> *curr;
node<Item> *prev = NULL;
// ICK alert: the below should be TWO calls to
// a helper function!!!!!
// remove target from source's adjacency list
curr = adj_to_vertex[source_index];
// if target is head of list...
if (curr->get_data( ) == target)
{
list_head_remove(adj_to_vertex[source_index]);
}
else
{
// target is "later" in list;
while (curr->get_data( ) != target)
{
prev = curr;
curr = curr->get_next( );
}
// cannot reach here unless curr->get_data( ) == target
// set node before target node to point to
// node after target
prev->set_next( curr->get_next( ) );
delete curr;
curr = NULL;
}
// remove source from target's adjacency list
curr = adj_to_vertex[target_index];
// if source is head of list...
if (curr->get_data( ) == source)
{
list_head_remove(adj_to_vertex[target_index]);
}
else
{
// source is "later" in list;
while (curr->get_data( ) != source)
{
prev = curr;
curr = curr->get_next( );
}
// cannot reach here unles curr->get_data( ) == source
// set node before source node to point to
// node after source
prev->set_next( curr->get_next( ) );
delete curr;
curr = NULL;
}
}
/****************************************************/
/* OVERLOADED OPERATORS */
/****************************************************/
// operator =
//
// postcondition: the graph that activated this
// will be made to have the same vertices and
// edges as source
//
template <typename Item>
void graph<Item>::operator =(const graph<Item>& source)
{
node<Item> *tail_ptr;
if (this == &source)
return;
copy(source.vertices, source.vertices + source.total_vertices,
vertices);
for (int i=0; i<graph<Item>::MAXIMUM; i++)
{
list_clear(adj_to_vertex[i]);
}
for (int i=0; i<source.total_vertices; i++)
{
list_copy(source.adj_to_vertex[i], adj_to_vertex[i], tail_ptr);
tail_ptr = NULL;
}
total_vertices = source.total_vertices;
total_edges = source.total_edges;
}
/****************************************************/
/* PRIVATE HELPER MEMBER FUNCTIONS */
/****************************************************/
// get_vert_index
//
// precondition: is_vertex(vert) == true
// postconditions: returns the index into adj_to_vert
// for vertex vert.
//
template <typename Item>
size_t graph<Item>::get_vert_index(Item vert) const
{
assert( is_vertex(vert) == true );
// look to find where vert is found in vertices array
// (note: this WILL stop early, once vert is found)
for (int i=0; i < total_vertices; i++)
{
if (vertices[i] == vert)
{
return i; // i is vert's index in vertices
}
}
}