Please send questions to
st10@humboldt.edu .
/* Cs132AdjListGraph.java 1.0 */
/* by Sharon Tuttle */
/* last modified: 5-02-03 */
/* */
/* an adjacency-list implementation of Cs132Graph */
public class Cs132AdjListGraph implements Cs132Graph
{
/*-----------------------------------------------------------------
fields
-------------------------------------------------------------------*/
int numVertices;
int numEdges;
int maxSize;
// need an array to map a vertex to its adj list index?
// or --- a little hash table?! but then vertex would have to be
// hashable; that would be efficient, but, for expediency:
// decision: an array of Cs132Vertex instances --- the index
// where a vertex is is the index in adjacency list where vertices
// adjacent to it are indicated
Cs132Vertex vertexList[];
// the adjacency list itself...
Cs132AdjListNode adjacencyList[];
/*---------------------------------------------------------------
constructor
-----------------------------------------------------------------*/
/*---------------------------------------------------------------
(for convenience, we'll take one argument: the desired maximum
number of vertices for the empty graph being created)
-----------------------------------------------------------------*/
public Cs132AdjListGraph(int maxNumVertices)
{
this.vertexList = new Cs132Vertex[maxNumVertices];
this.adjacencyList = new Cs132AdjListNode[maxNumVertices];
this.numVertices = 0;
this.numEdges = 0;
this.maxSize = maxNumVertices;
}
/*-----------------------------------------------------------------
isEmpty()
Purpose: return true if there are no vertices and no edges in the
graph, false otherwise.
---------------------------------------------------------------------*/
public boolean isEmpty()
{
// note that if there are no vertices, there can be no
// edges, either...
return (this.numVertices == 0);
}
/*-----------------------------------------------------------
vertexCount()
Purpose: return the number of vertices in this graph.
-------------------------------------------------------------*/
public int vertexCount()
{
return this.numVertices;
}
/*-----------------------------------------------------------
edgeCount()
Purpose: return the number of edges in this graph.
-------------------------------------------------------------*/
public int edgeCount()
{
return this.numEdges;
}
/*-----------------------------------------------------------
containsEdge()
Purpose: determine whether an edge exists between two given
vertices v1 and v2 --- return true if so, false
otherwise.
-------------------------------------------------------------*/
public boolean containsEdge(Cs132Vertex v1, Cs132Vertex v2)
{
int v1AdjListIndex = getIndex(v1);
int v2AdjListIndex = getIndex(v2);
// make sure these vertices are in graph...!
if ((v1AdjListIndex == -1) || (v2AdjListIndex == -1))
{
return false;
}
// if reach here --- the vertices are in the graph, anyway!
Cs132AdjListNode curr = adjacencyList[v1AdjListIndex];
// now, see if v2 is in v1's adjacency list;
while (curr != null)
{
// if I find v2 --- I'm done, there's an edge btwn v1 and v2!
if (curr.getVertex().getKey().compareTo(v2.getKey()) == 0)
{
return true;
}
// curr doesn't contain v2? keep going...
curr = curr.getNext();
}
// if get here --- no v2 in v1's adjacency list. There's
// no edge between 'em.
return false;
}
/*----------------------------------------------------------
getIndex()
Purpose: return the index of the adjacency list for the passed
vertex vert.
Return -1 if no index for this vertex is found.
--------------------------------------------------------------*/
public int getIndex(Cs132Vertex vert)
{
Comparable vertKey = vert.getKey();
// potentially might need to check this.vertexKeys for all
// currently-existing vertices in the graph
for (int i=0; i < this.numVertices; i++)
{
// if we have found the desired key --- then i is its index
if (vertKey.compareTo(this.vertexList[i].getKey()) == 0)
{
return i;
}
}
// if get here? vertKey wasn't in this.vertexIndices;
return -1;
}
/*------------------------------------------------------------
addVertex()
Purpose: add a vertex vert to the graph; it is expected that
a vertex has a search key, and that it is unique to
each vertex. So, if vert has the same search key as
an existing vertex already in the graph, it should not
be added to the graph.
---------------------------------------------------------------*/
public void addVertex(Cs132Vertex vert) throws GraphOverflowException
{
// first: make sure vertex isn't already in graph.
Comparable vertKey = vert.getKey();
int vertAdjListIndex = getIndex(vert);
if (vertAdjListIndex != -1)
{
// vertex with this key is already in graph --- we're done
return;
}
// if reach here, this vertex ISN'T in graph --- is there
// room for it? If not...
if (this.numVertices == this.maxSize)
{
throw new GraphOverflowException();
}
// if reach here, there IS room for the new vertex
vertAdjListIndex = numVertices;
numVertices++;
vertexList[vertAdjListIndex] = vert;
// (right now, NO edges are adjacent to new vert --
// so adjacencyList[vertAdjListIndex] is properly null!)
}
/*---------------------------------------------------------------
addEdge()
Purpose: add an edge between the two given vertices v1 and v2 to
the graph. (If such an edge already does exist, the graph
should not change as a result of this action.)
------------------------------------------------------------------*/
public void addEdge(Cs132Vertex v1, Cs132Vertex v2)
{
// first: get the adjacency list indices for both
// vertices
int v1AdjListIndex = getIndex(v1);
int v2AdjListIndex = getIndex(v2);
// if either has no such index, cannot add the edge
if ((v1AdjListIndex == -1) || (v2AdjListIndex == -1))
{
return; // probably should complain, but...
}
// if reach here, both vertices are in graph
// see if there is already an edge between v1 and v2
boolean isAlreadyEdge = containsEdge(v1, v2);
// if there ISN'T --- add the edge.
if (!isAlreadyEdge)
{
this.numEdges++;
//--------
// add v2 to v1's adjacency list
//--------
Cs132AdjListNode v1OldHead = this.adjacencyList[v1AdjListIndex];
Cs132AdjListNode v2NewNode = new Cs132AdjListNode(v2);
// make v2-node new first node in v1's adjacency list
this.adjacencyList[v1AdjListIndex] = v2NewNode;
v2NewNode.setNext(v1OldHead);
//--------
// add v1 to v2's adjacency list
//--------
Cs132AdjListNode v2OldHead = this.adjacencyList[v2AdjListIndex];
Cs132AdjListNode v1NewNode = new Cs132AdjListNode(v1);
// make v1-node new first node in v2's adjacency list
this.adjacencyList[v2AdjListIndex] = v1NewNode;
v1NewNode.setNext(v2OldHead);
}
}
/*-----------------------------------------------------------------
removeVertex()
Purpose: remove/delete the specified vertex vert from the graph,
along with any edges between vert and other vertices.
-------------------------------------------------------------------*/
public void removeVertex(Cs132Vertex vert)
{
// first: IS the vertex even in the graph?
int vertAdjListIndex = getIndex(vert);
if (vertAdjListIndex == -1)
{
return; // vertex is already removed/not in graph
}
// if reach here --- vertex IS in graph.
// walk through vertex-to-be-removed's adjacency list,
// removing each edge adjacent to this vertex
Cs132AdjListNode curr = this.adjacencyList[vertAdjListIndex];
while (curr != null)
{
Cs132Vertex otherVert = curr.getVertex();
curr = curr.getNext(); // do this BEFORE edge destroyed by...
this.removeEdge(vert, otherVert); // this'll properly update
// this.numEdges, too
}
// remove it from this.vertexList and this.adjacencyList by
// shifting any after it to fill the resulting "hole"
for (int i = vertAdjListIndex; i < this.numVertices; i++)
{
this.vertexList[i] = this.vertexList[i+1];
this.adjacencyList[i] = this.adjacencyList[i+1];
}
this.numVertices--;
}
/*--------------------------------------------------------------------
removeEdge()
Purpose: remove/delete the edge, if any, between vertices v1 and v2.
---------------------------------------------------------------------*/
public void removeEdge(Cs132Vertex v1, Cs132Vertex v2)
{
// IS there an edge between them to remove?
boolean isEdge = this.containsEdge(v1, v2);
// if not, we're done...
if (!isEdge)
{
return;
}
// if get here, there IS an edge to remove;
// get the adjacency list indices for both
// vertices
int v1AdjListIndex = getIndex(v1);
int v2AdjListIndex = getIndex(v2);
// remove v2 from v1's adjacency list
Cs132AdjListNode prev = null;
Cs132AdjListNode curr = this.adjacencyList[v1AdjListIndex];
while (curr != null)
{
if (curr.getVertex().getKey().compareTo(v2.getKey()) == 0)
{
// found v2 node! remove it;
// see if v2 node is at beginning of adjacency list
if (prev == null)
{
this.adjacencyList[v1AdjListIndex] = curr.getNext();
}
// otherwise, remove it from after-beginning of list
else
{
prev.setNext(curr.getNext());
}
}
// this isn't v2 node --- move on down
else
{
prev = curr;
curr = curr.getNext();
}
}
// remove v1 from v2's adjacency list
prev = null;
curr = this.adjacencyList[v2AdjListIndex];
while (curr != null)
{
if (curr.getVertex().getKey().compareTo(v1.getKey()) == 0)
{
// found v1 node! remove it;
// see if v1 node is at beginning of adjacency list
if (prev == null)
{
this.adjacencyList[v2AdjListIndex] = curr.getNext();
}
// otherwise, remove it from after-beginning of list
else
{
prev.setNext(curr.getNext());
}
}
// this isn't v1 node --- move on down
else
{
prev = curr;
curr = curr.getNext();
}
}
// whew --- it's removed --- reduce count of edges!
this.numEdges--;
}
/*-------------------------------------------------------------
getVertex()
Purpose: gets the vertex with the specified search key from the
graph. Returns null if there is no such vertex.
(I decided to make search key comparable because,
to be able to compare two keys, it seemed to be a
reasonable requirement...)
---------------------------------------------------------------*/
public Cs132Vertex getVertex(Comparable key)
{
Cs132Vertex currVert = null;
for (int i=0; i < this.numVertices; i++)
{
currVert = vertexList[i];
if (currVert.getKey().compareTo(key) == 0)
{
// found desired vertex!
return currVert;
}
}
// if get here --- vertex wasn't in list of vertices,
// so isn't in graph.
return null;
}
/*-------------------------------------------------------------------
clear()
Purpose: removes all vertices from the graph (with the implication
that, as they are removed, edges associated with them will
be removed, also).
---------------------------------------------------------------------*/
public void clear()
{
// thanks to the wonders of garbage collection, this
// might actually work...
for (int i = 0; i < this.numVertices; i++)
{
this.adjacencyList[i] = null;
this.vertexList[i] = null;
}
this.numVertices = 0;
this.numEdges = 0;
}
/*----------------------------------------------------------------
reset()
Purpose: "clears" the visited-flags within the vertices
of this graph, causing all vertices to be marked
as unvisited.
------------------------------------------------------------------*/
public void reset()
{
// mark the visited-flags within vertices in the vertex
// list
for (int i=0; i < this.numVertices; i++)
{
this.vertexList[i].setVisited(false);
}
// question: are the visited flags within the adjacency list
// (inside the Cs132AdjListNode's) ever used?
// JUST in case they are --- let's clear 'em out!
for (int i=0; i < this.numVertices; i++)
{
Cs132AdjListNode curr = this.adjacencyList[i];
while (curr != null)
{
curr.getVertex().setVisited(false);
curr = curr.getNext();
}
}
}
/*----------------------------------------------------------------
degree()
Purpose: returns the number of vertices adjacent to the given
vertex vert; what shall we do if there IS no such
vertex? Hm; let's try returning -1 in that case.
------------------------------------------------------------------*/
public int degree(Cs132Vertex vert)
{
int vertAdjListIndex = getIndex(vert);
// if vert is NOT a vertex in this graph, return a degree of -1
if (vertAdjListIndex == -1)
{
return -1;
}
// if get here --- vert IS in graph;
int numAdjacent = 0;
Cs132AdjListNode curr = this.adjacencyList[vertAdjListIndex];
// each non-null curr represents a vertex that vert is
// adjacent to;
while (curr != null)
{
numAdjacent++;
curr = curr.getNext();
}
return numAdjacent;
}
/*--------------------------------------------------
adjacentVerts()
Purpose: return an array containing the vertices adjacent
to the given vertex vert; return null if
are none adjacent, or if vert is not in graph.
----------------------------------------------------*/
public Cs132Vertex[] adjacentVerts (Cs132Vertex vert)
{
int vertAdjListIndex = getIndex(vert);
// if vert is NOT a vertex in this graph, return null
if (vertAdjListIndex == -1)
{
return null;
}
// if get here --- vert IS in graph
int vertDegree = this.degree(vert);
// if vert IS in graph, but has no vertices adjacent to it,
// return null, too. (Could have combined with above --- BUT
// I'm separating in case I decide to later throw an exception
// for the above case...)
if (vertDegree == 0)
{
return null;
}
// if get here --- vert DOES have adjacent vertices
Cs132Vertex adjVerts[] = new Cs132Vertex[vertDegree];
int nextVertIndex = 0;
Cs132AdjListNode curr = this.adjacencyList[vertAdjListIndex];
// each non-null curr represents a vertex that vert is
// adjacent to;
while (curr != null)
{
adjVerts[nextVertIndex] = curr.getVertex();
nextVertIndex++;
curr = curr.getNext();
}
return adjVerts;
}
/*------------------------------------------------------------------
toString()
Purpose: give a string depiction of this graph...!
Think I'll try it in G = (V, E) form...
--------------------------------------------------------------------*/
public String toString()
{
this.reset(); // need all nodes unvisited, to avoid duplicating
// edges in String depiction
String returnString = "Cs132AdjListGraph[# vertices: " +
this.numVertices + ", # edges: " +
this.numEdges + ", vertex capacity: "
+ this.maxSize + "\n V = (";
// build string depiction of vertices...
for (int i = 0; i < this.numVertices; i++)
{
returnString += "[" + this.vertexList[i].getKey() + "]";
}
returnString += ")\n E = (";
// build string depiction of edges...
for (int i=0; i < this.numVertices; i++)
{
Cs132Vertex currVert = this.vertexList[i];
Comparable currVertKey = currVert.getKey();
Cs132AdjListNode currNode = this.adjacencyList[i];
while (currNode != null)
{
// if vertex is marked as visited --- its edge
// written other-vertex-first has already been
// put into string. So, if has NOT been visited...
/* if (currNode.getVertex().getVisited() == false)
{*/
returnString += "(" + currVertKey + ", " +
currNode.getVertex().getKey() + ") ";
/*currNode.getVertex().setVisited(true);*/
// ICK --- gotta FIND edge from other "direction",
// mark IT as visited, too --- ICK.
/* int currNodeAdjListIndex = this.getIndex(
currNode.getVertex());
Cs132AdjListNode otherNode = this.adjacencyList[
currNodeAdjListIndex];
boolean found = false;
while ((!found) && (otherNode != null))
{
if (otherNode.getVertex().getKey().compareTo(
currVertKey) == 0)
{
otherNode.getVertex().setVisited(true);
found = true;
}
else
{
otherNode = otherNode.getNext();
}
}
if (found == false)
{
returnString += "EEK! didn't find corresp. "
+ "equivalent edge!\n";
}
}
*/
currNode = currNode.getNext();
}
}
returnString += ")\n]\n";
// mark all nodes as unvisited again
this.reset();
return returnString;
}
}