import java.util.*;
public abstract class AbstractGraph implements Graph {
protected List vertices = new ArrayList(); // Store vertices
protected List> neighbors
= new ArrayList>(); // Adjacency lists
/** Construct an empty graph */
protected AbstractGraph() {
}
/** Construct a graph from edges and vertices stored in arrays */
protected AbstractGraph(int[][] edges, V[] vertices) {
for (int i = 0; i < vertices.length; i++)
this.vertices.add(vertices[i]);
createAdjacencyLists(edges, vertices.length);
}
/** Construct a graph from edges and vertices stored in List */
protected AbstractGraph(List edges, List vertices) {
for (int i = 0; i < vertices.size(); i++)
this.vertices.add(vertices.get(i));
createAdjacencyLists(edges, vertices.size());
}
/** Construct a graph for integer vertices 0, 1, 2 and edge list */
protected AbstractGraph(List edges, int numberOfVertices) {
for (int i = 0; i < numberOfVertices; i++) {
vertices.add((V)(new Integer(i))); // vertices is {0, 1, ...}
}
createAdjacencyLists(edges, numberOfVertices);
}
/** Construct a graph from integer vertices 0, 1, and edge array */
protected AbstractGraph(int[][] edges, int numberOfVertices) {
for (int i = 0; i < numberOfVertices; i++) {
vertices.add((V)(new Integer(i))); // vertices is {0, 1, ...}
}
createAdjacencyLists(edges, numberOfVertices);
}
/** Create adjacency lists for each vertex */
private void createAdjacencyLists(
int[][] edges, int numberOfVertices) {
// Create a linked list
for (int i = 0; i < numberOfVertices; i++) {
neighbors.add(new ArrayList());
}
for (int i = 0; i < edges.length; i++) {
int u = edges[i][0];
int v = edges[i][1];
neighbors.get(u).add(v);
}
}
/** Create adjacency lists for each vertex */
private void createAdjacencyLists(
List edges, int numberOfVertices) {
// Create a linked list for each vertex
for (int i = 0; i < numberOfVertices; i++) {
neighbors.add(new ArrayList());
}
for (Edge edge: edges) {
neighbors.get(edge.u).add(edge.v);
}
}
/** Return the number of vertices in the graph */
public int getSize() {
return vertices.size();
}
/** Return the vertices in the graph */
public List getVertices() {
return vertices;
}
/** Return the object for the specified vertex */
public V getVertex(int index) {
return vertices.get(index);
}
/** Return the index for the specified vertex object */
public int getIndex(V v) {
return vertices.indexOf(v);
}
/** Return the neighbors of vertex with the specified index */
public List getNeighbors(int index) {
return neighbors.get(index);
}
/** Return the degree for a specified vertex */
public int getDegree(int v) {
return neighbors.get(v).size();
}
/** Print the edges */
public void printEdges() {
for (int u = 0; u < neighbors.size(); u++) {
System.out.print(getVertex(u) + " (" + u + "): ");
for (int j = 0; j < neighbors.get(u).size(); j++) {
System.out.print("(" + u + ", " +
neighbors.get(u).get(j) + ") ");
}
System.out.println();
}
}
/** Clear graph */
public void clear() {
vertices.clear();
neighbors.clear();
}
/** Add a vertex to the graph */
public void addVertex(V vertex) {
vertices.add(vertex);
neighbors.add(new ArrayList());
}
/** Add an edge to the graph */
public void addEdge(int u, int v) {
neighbors.get(u).add(v);
neighbors.get(v).add(u);
}
/** Edge inner class inside the AbstractGraph class */
public static class Edge {
public int u; // Starting vertex of the edge
public int v; // Ending vertex of the edge
/** Construct an edge for (u, v) */
public Edge(int u, int v) {
this.u = u;
this.v = v;
}
}
/** Obtain a DFS tree starting from vertex v */
public Tree dfs(int v) {
List searchOrders = new ArrayList();
int[] parent = new int[vertices.size()];
for (int i = 0; i < parent.length; i++)
parent[i] = -1; // Initialize parent[i] to -1
// Mark visited vertices
boolean[] isVisited = new boolean[vertices.size()];
// Recursively search
dfs(v, parent, searchOrders, isVisited);
// Return a search tree
return new Tree(v, parent, searchOrders);
}
/** Recursive method for DFS search */
private void dfs(int v, int[] parent, List searchOrders,
boolean[] isVisited) {
// Store the visited vertex
searchOrders.add(v);
isVisited[v] = true; // Vertex v visited
for (int i : neighbors.get(v)) {
if (!isVisited[i]) {
parent[i] = v; // The parent of vertex i is v
dfs(i, parent, searchOrders, isVisited); // Recursive search
}
}
}
/** Starting bfs search from vertex v */
/** To be discussed in Section 27.7 */
public Tree bfs(int v) {
List searchOrders = new ArrayList();
int[] parent = new int[vertices.size()];
for (int i = 0; i < parent.length; i++)
parent[i] = -1; // Initialize parent[i] to -1
java.util.LinkedList queue =
new java.util.LinkedList(); // list used as a queue
boolean[] isVisited = new boolean[vertices.size()];
queue.offer(v); // Enqueue v
isVisited[v] = true; // Mark it visited
while (!queue.isEmpty()) {
int u = queue.poll(); // Dequeue to u
searchOrders.add(u); // u searched
for (int w : neighbors.get(u)) {
if (!isVisited[w]) {
queue.offer(w); // Enqueue w
parent[w] = u; // The parent of w is u
isVisited[w] = true; // Mark it visited
}
}
}
return new Tree(v, parent, searchOrders);
}
/** Tree inner class inside the AbstractGraph class */
/** To be discussed in Section 27.5 */
public class Tree {
private int root; // The root of the tree
private int[] parent; // Store the parent of each vertex
private List searchOrders; // Store the search order
/** Construct a tree with root, parent, and searchOrder */
public Tree(int root, int[] parent, List searchOrders) {
this.root = root;
this.parent = parent;
this.searchOrders = searchOrders;
}
/** Return the root of the tree */
public int getRoot() {
return root;
}
/** Return the parent of vertex v */
public int getParent(int v) {
return parent[v];
}
/** Return an array representing search order */
public List getSearchOrders() {
return searchOrders;
}
/** Return number of vertices found */
public int getNumberOfVerticesFound() {
return searchOrders.size();
}
/** Return the path of vertices from a vertex index to the root */
public List getPath(int index) {
ArrayList path = new ArrayList();
do {
path.add(vertices.get(index));
index = parent[index];
}
while (index != -1);
return path;
}
/** Print a path from the root to vertex v */
public void printPath(int index) {
List path = getPath(index);
System.out.print("A path from " + vertices.get(root) + " to " +
vertices.get(index) + ": ");
for (int i = path.size() - 1; i >= 0; i--)
System.out.print(path.get(i) + " ");
}
/** Print the whole tree */
public void printTree() {
System.out.println("Root is: " + vertices.get(root));
System.out.print("Edges: ");
for (int i = 0; i < parent.length; i++) {
if (parent[i] != -1) {
// Display an edge
System.out.print("(" + vertices.get(parent[i]) + ", " +
vertices.get(i) + ") ");
}
}
System.out.println();
}
}
/** Return a Hamiltonian path from the specified vertex object
* Return null if the graph does not contain a Hamiltonian path */
public List getHamiltonianPath(V vertex) {
return getHamiltonianPath(getIndex(vertex));
}
/** Return a Hamiltonian path from the specified vertex label
* Return null if the graph does not contain a Hamiltonian path */
public List getHamiltonianPath(int v) {
// A path starts from v. (i, next[i]) represents an edge in
// the path. isVisited[i] tracks whether i is currently in the
// path.
int[] next = new int[getSize()];
for (int i = 0; i < next.length; i++)
next[i] = -1; // Indicate no subpath from i is found yet
boolean[] isVisited = new boolean[getSize()];
// The vertices in the Hamiltonian path are stored in result
List result = null;
// To speed up search, reorder the adjacency list for each
// vertex so that the vertices in the list are in increasing
// order of their degrees
for (int i = 0; i < getSize(); i++)
reorderNeigborsBasedOnDegree(neighbors.get(i));
if (getHamiltonianPath(v, next, isVisited)) {
result = new ArrayList(); // Create a list for path
int vertex = v; // Starting from v
while (vertex != -1) {
result.add(vertex); // Add vertex to the result list
vertex = next[vertex]; // Get the next vertex in the path
}
}
return result; // return null if no Hamiltonian path is found
}
/** Reorder the adjacency list in increasing order of degrees */
private void reorderNeigborsBasedOnDegree(List list) {
for (int i = list.size() - 1; i >= 1; i--) {
// Find the maximum in the list[0..i]
int currentMaxDegree = getDegree(list.get(0));
int currentMaxIndex = 0;
for (int j = 1; j <= i; j++) {
if (currentMaxDegree < getDegree(list.get(j))) {
currentMaxDegree = getDegree(list.get(j));
currentMaxIndex = j;
}
}
// Swap list[i] with list[currentMaxIndex] if necessary;
if (currentMaxIndex != i) {
int temp = list.get(currentMaxIndex);
list.set(currentMaxIndex, list.get(i));
list.set(i, temp);
}
}
}
/** Return true if all elements in array isVisited are true */
private boolean allVisited(boolean[] isVisited) {
boolean result = true;
for (int i = 0; i < getSize(); i++)
result = result && isVisited[i];
return result;
}
/** Search for a Hamiltonian path from v */
private boolean getHamiltonianPath(int v, int[] next,
boolean[] isVisited) {
isVisited[v] = true; // Mark vertex v visited
if (allVisited(isVisited))
return true; // The path now includes all vertices, thus found
for (int i = 0; i < neighbors.get(v).size(); i++) {
int u = neighbors.get(v).get(i);
if (!isVisited[u] &&
getHamiltonianPath(u, next, isVisited)) {
next[v] = u; // Edge (v, u) is in the path
return true;
}
}
isVisited[v] = false; // Backtrack, v is marked unvisited now
return false; // No Hamiltonian path exists from vertex v
}
}
public interface Graph {
/** Return the number of vertices in the graph */
public int getSize();
/** Return the vertices in the graph */
public java.util.List getVertices();
/** Return the object for the specified vertex index */
public V getVertex(int index);
/** Return the index for the specified vertex object */
public int getIndex(V v);
/** Return the neighbors of vertex with the specified index */
public java.util.List getNeighbors(int index);
/** Return the degree for a specified vertex */
public int getDegree(int v);
/** Print the edges */
public void printEdges();
/** Clear graph */
public void clear();
/** Add a vertex to the graph */
public void addVertex(V vertex);
/** Add an edge to the graph */
public void addEdge(int u, int v);
/** Obtain a depth-first search tree */
public AbstractGraph.Tree dfs(int v);
/** Obtain a breadth-first search tree */
public AbstractGraph.Tree bfs(int v);
}
public class TestGraph {
public static void main(String[] args) {
String[] vertices = {"Seattle", "San Francisco", "Los Angeles",
"Denver", "Kansas City", "Chicago", "Boston", "New York",
"Atlanta", "Miami", "Dallas", "Houston"};
// Edge array for graph
int[][] edges = {
{0, 1}, {0, 3}, {0, 5},
{1, 0}, {1, 2}, {1, 3},
{2, 1}, {2, 3}, {2, 4}, {2, 10},
{3, 0}, {3, 1}, {3, 2}, {3, 4}, {3, 5},
{4, 2}, {4, 3}, {4, 5}, {4, 7}, {4, 8}, {4, 10},
{5, 0}, {5, 3}, {5, 4}, {5, 6}, {5, 7},
{6, 5}, {6, 7},
{7, 4}, {7, 5}, {7, 6}, {7, 8},
{8, 4}, {8, 7}, {8, 9}, {8, 10}, {8, 11},
{9, 8}, {9, 11},
{10, 2}, {10, 4}, {10, 8}, {10, 11},
{11, 8}, {11, 9}, {11, 10}
};
Graph graph1 =
new UnweightedGraph(edges, vertices);
System.out.println("The number of vertices in graph1: "
+ graph1.getSize());
System.out.println("The vertex with index 1 is "
+ graph1.getVertex(1));
System.out.println("The index for Miami is " +
graph1.getIndex("Miami"));
System.out.println("The edges for graph1:");
graph1.printEdges();
// List of Edge objects for graph in Figure 27.3(a)
String[] names = {"Peter", "Jane", "Mark", "Cindy", "Wendy"};
java.util.ArrayList edgeList
= new java.util.ArrayList();
edgeList.add(new AbstractGraph.Edge(0, 2));
edgeList.add(new AbstractGraph.Edge(1, 2));
edgeList.add(new AbstractGraph.Edge(2, 4));
edgeList.add(new AbstractGraph.Edge(3, 4));
// Create a graph with 5 vertices
Graph graph2 = new UnweightedGraph
(edgeList, java.util.Arrays.asList(names));
System.out.println("\nThe number of vertices in graph2: "
+ graph2.getSize());
System.out.println("The edges for graph2:");
graph2.printEdges();
}
}
Comments:
This design can be criticized in a some ways. By design I mean choosing to make a abstract class, interface, and a concrete class. So the goal of this is to make a small usable graphs/graph algorithms api in java for display purposes. If we really want to implement provably fast algorithms, we need to reify the design from the perspective of minimizing both time and space. With that end in mind, in the near future, I will present
a nonrecursive version of DFS
a method to read in a graph from a file containing data in the following format: v1-v2,v3,v4,v4;
solutions to the Euler path problem, Hamiltonian path problem, topological sort