import java.util.*;
import java.text.DecimalFormat;
import java.awt.Color;
import java.awt.Graphics;
/**
* Graph.java, models graph management and creation.
* Implements: GraphInterface.
* @author Matt Markham
*/
public class Graph implements GraphInterface
{
//size integer variable to keep track of number of nodes in the graph
private int size;
//Double two-dimensional array for the adjacency matrix
private double[][] adjacent;
//boolean to determine whether a graph is directed
private boolean directed;
//Vertex storage list
private ArrayList vertexList;
//int to determin generic unweighted value
private final int UNWEIGHTED_VALUE = 1;
//temporary Arraylist for dfs
private ArrayList tempAL=new ArrayList();
//ArrayList containing the current shortest path
private ArrayList path;
/**
* Default Constructor, creates an undirected graph.
*/
public Graph ()
{
size = 0;
adjacent = new double[size][size];
vertexList = new ArrayList();
directed=false;
path = new ArrayList();
}
/**
* Constructor, takes a boolean to determine directed or undirected.
*/
public Graph (boolean param)
{
size = 0;
adjacent = new double[size][size];
vertexList = new ArrayList();
directed=param;
path = new ArrayList();
}
public void makeEmpty()
{
size=0;
adjacent = new double[size][size];
vertexList.clear();
}
public boolean isEmpty()
{
return (size==0);
}
public int numVertices()
{
return size;
}
public int numEdges()
{
int c=0;
for(int x=0; x
* Preconditions: must be passed a new size value.
* Postconditions: resizes adjacency matrix.
* Throws: None.
*/
private void resizeAdjacent ( int size )
{
double[][] temp = new double[size][size];
for( int x = 0; x < size - 1; x++)
for ( int y = 0; y < size - 1; y++ )
temp[x][y] = adjacent[x][y];
for ( int x = 0; x < size; x++ )
{
temp[x][size - 1] = Double.POSITIVE_INFINITY;
temp[size - 1][x] = Double.POSITIVE_INFINITY;
}
adjacent = temp;
}
public void addEdge(Comparable searchKey1, Comparable searchKey2, double weight) throws GraphException
{
for(int x=0; xsize)
throw new GraphException ("index out of bounds!");
else
return (GraphNode)vertexList.get(index);
}
public GraphNode getVertex(Comparable searchKey) throws GraphException
{
for (int x=0;x
* Preconditions: Must be passed searchKey.
* Postconditions: returns integer index of given searchKey.
* Throws:Nonde.
*/
private int findVertex (Comparable searchKey)
{
for (int d=0; d
* Preconditions: None.
* Postconditions: Makes all vertices unmarked.
* Throws:None.
*/
private void clearMarks()
{
for (int x=0;x index )
newrow = row - 1;
if ( col > index )
newcol = col - 1;
temp[newrow][newcol] = adjacent[row][col];
}
}
}
adjacent = temp;
size--;
return (GraphNode) vertexList.remove ( index );
}
public ArrayList dfs (Comparable searchKey) throws GraphException
{
ArrayList dfsList = new ArrayList();
dfsList=dfsRec(getVertex(searchKey), dfsList);
clearMarks();
return dfsList;
}
/**
* Private recursive method called to generate a breadth-first search.
* Preconditions: Must be passed a graphNode and an ArrayList.
* Postconditions: Returns an Arraylist for the bfs.
* Throws:None.
*/
private ArrayList dfsRec(GraphNode vertex, ArrayList searchRecList)
{
searchRecList.add(vertex);
vertex.setMarked(true);
int i=vertexList.indexOf(vertex);
for(int j=0; j
* Preconditions: A Graphics object and the radius of the vertecies.
* Postconditions: Draws all of the neccessary components onto the GraphScreen.
* @author Andrew Coleman
*/
public void draw ( Graphics g, int radius )
{
/* draw all of the edges in orange */
g.setColor ( Color.orange );
for ( int i = 0; i < adjacent.length; i++ )
{
int firstx = getVertex ( i ).getX();
int firsty = getVertex ( i ).getY();
String firstkey = getVertex ( i ).getKey().toString();
for ( int j = 0; j < adjacent[i].length; j++ )
{
if ( adjacent[i][j] != Double.POSITIVE_INFINITY )
{
int secondx = ((GraphNode) getVertex ( j )).getX();
int secondy = ((GraphNode) getVertex ( j )).getY();
String secondkey = (String)((GraphNode) getVertex ( j )).getKey();
DecimalFormat format = new DecimalFormat();
format.setMaximumFractionDigits ( 3 );
int difference = 10;
/* the final x and y coordinates for the string for each edge */
int xcoord, ycoord;
if ( firstx < secondx )
xcoord = (secondx - firstx) / 2 + firstx;
else
xcoord = (firstx - secondx) / 2 + secondx;
if ( firsty < secondy )
ycoord = (secondy - firsty) / 2 + firsty;
else
ycoord = (firsty - secondy) / 2 + secondy;
/* if i < j, then the edge goes from j to i */
if ( i < j )
{
g.drawLine ( firstx, firsty - 10, secondx, secondy - 10 );
g.drawString ( firstkey + " to " + secondkey + ", Weight: " + format.format ( adjacent[i][j] ), xcoord, ycoord - 11 );
}
/* if i > j, then the edge goes from i to j */
else if ( i > j )
{
g.drawLine ( firstx, firsty + 10, secondx, secondy + 10 );
g.drawString ( firstkey + " to " + secondkey + ", Weight: " + format.format ( adjacent[i][j] ), xcoord, ycoord + 11 );
}
}
}
}
/* draw the shortest path stored in green */
g.setColor ( Color.green );
for ( int i = 1; i < path.size(); i++ )
{
int firstx = ((GraphNode) path.get ( i - 1 )).getX();
int firsty = ((GraphNode) path.get ( i - 1 )).getY();
int secondx = ((GraphNode) path.get ( i )).getX();
int secondy = ((GraphNode) path.get ( i )).getY();
g.drawLine ( firstx, firsty, secondx, secondy );
}
/* draw all of the vertices in dark gray with a light gray border and the comparable data in the middle */
for ( int i = 0; i < numVertices(); i++ )
{
g.setColor ( Color.lightGray );
GraphNode temp = getVertex ( i );
g.fillOval ( temp.getX() - radius, temp.getY() - radius, radius * 2, radius * 2 );
g.setColor ( Color.darkGray );
int difference = 2;
g.fillOval ( temp.getX() - radius + difference, temp.getY() - radius + difference, (radius - difference) * 2, (radius - difference) * 2 );
g.setColor ( Color.white );
g.drawString ( (String) temp.getKey(), temp.getX(), temp.getY() );
}
}
/**
* This method is used to set the arraylist containing the shortest path to be displayed.
* Preconditions: An arraylist containing a valid shortest path.
* Postconditions: Sets the current path to the given arraylist.
* @author Andrew Coleman
*/
public void setShortestPathDisplay ( ArrayList path )
{
this.path = path;
}
/**
* Used to calculate the shortest distance between two vertices using Djisktra's algorithm.
* Preconditions: Two comparables representing the data in the two vertices.
* Postconditions: Returns an ArrayList containing the GraphNodes in the path.
* Throws: Throws a GraphException if the comparables are the same or there is no connecting path.
* @author Andrew Coleman
*/
public ArrayList shortestPath ( Comparable firstkey, Comparable lastkey ) throws GraphException {
if ( firstkey.compareTo ( lastkey ) == 0 )
throw new GraphException ( "Cannot find shortest path to same vertex!" );
/* the arraylist containing the raw path array */
ArrayList path = new ArrayList ( vertexList.size() );
/* mark the first vertex */
getVertex ( firstkey ).setMarked ( true );
/* indexes of the first and last search keys */
int firstindex = findIndex ( firstkey );
int secondindex = findIndex ( lastkey );
/* the row of the adjacency matrix at firstindex */
double[] weight = new double[vertexList.size()];
/* initialize the weight/path arrays */
for ( int i = 0; i < weight.length; i++ )
{
path.add ( vertexList.get ( i ) );
weight[i] = adjacent[firstindex][i];
/* if the weight is not infinity we must change the path to reflect the changes */
if ( weight[i] < Double.POSITIVE_INFINITY )
path.set ( i, getVertex ( firstkey ) );
}
/* loop through all the elements in the graph, must mark them all */
for ( int i = 1; i < vertexList.size(); i++ )
{
/* index of smallest vertex */
int smallest = -1;
/* smallest weight thus far that is not marked */
double smallestweight = Double.POSITIVE_INFINITY;
for ( int j = 0; j < weight.length; j++ )
/* run through the weight array and find the smallest weight */
if ( smallestweight >= weight[j] && !((GraphNode) vertexList.get ( j )).isMarked() )
{
/* note smallest vertex */
smallest = j;
smallestweight = weight[j];
}
/* mark smallest vertex */
GraphNode smallnode = (GraphNode) vertexList.get ( smallest );
smallnode.setMarked ( true );
/* update the weight/path arrays */
for ( int j = 0; j < weight.length; j++ )
/* if a new weight to that vertex is less than the current weight, change the weight in the array and change the path arraylist */
if ( weight[j] > weight[smallest] + adjacent[smallest][j] )
{
weight[j] = weight[smallest] + adjacent[smallest][j];
path.set ( j, vertexList.get ( smallest ) );
}
}
/* backwards path */
ArrayList result = new ArrayList();
GraphNode node = getVertex ( lastkey );
int lastindex = findIndex ( lastkey );
/* while the node is not the first node */
while ( lastindex != firstindex )
{
result.add ( node );
node = (GraphNode) path.get ( lastindex );
int newlastindex = findIndex ( node.getKey() );
/* if the indexes are the same, then there is no way to get to that vertex */
if ( newlastindex == lastindex )
throw new GraphException ( "No connecting path!" );
else
lastindex = newlastindex;
}
/* gotta add the first one */
result.add ( getVertex ( firstkey ) );
/* the result to return */
ArrayList sortedresult = new ArrayList ( result.size() );
/* reverse the arraylist */
for ( int i = result.size() - 1; i >= 0; i-- )
sortedresult.add ( result.get ( i ) );
return sortedresult;
}
/**
* This method finds the index of a given comparable object.
* Preconditions: A valid comparable key in the graph.
* Postcondtions: Returns the index of the key, or -1 if the key is not in the graph.
*/
private int findIndex ( Comparable key )
{
for ( int i = 0; i < vertexList.size(); i++ )
if ( ((GraphNode) vertexList.get ( i )).getKey().compareTo ( key ) == 0 )
return i;
return -1;
}
}