import java.util.*; public class myListGraph { protected String[] names; // 1-d array to store the vertices protected StringLinkList[] Edges; // 1-d array to store adjacencies between vertices, protected int numVertices; protected int numEdges; // Default constructor. Sets aside enough capacity for one vertex public myListGraph( ) { this(1); } // Constructor that sets aside as much capacity as specified by the user public myListGraph(int capacity) { names = new String[capacity]; Edges = new StringLinkList[capacity]; for (int i = 0 ; i < capacity ; i ++) { Edges[i] = new StringLinkList(); } } public int numberOfVertices() { return numVertices; } public int numberOfEdges() { return numEdges; } // Finds the location at which a vertex is stored in Vertices. // Returns -1 if vertex not found public int getIndex(String vertex) { for(int i = 0; i < numVertices; i++) if(vertex.equals(names[i])) return i; return -1; } // Resizes the array of vertices. Can make it larger or smaller, depending // on what newSize is. protected String[] resize(String[] array, int newSize) { String[] temp = new String[newSize]; int smallerSize = newSize; if(array.length < smallerSize) smallerSize = array.length; for(int i = 0; i < smallerSize; i++) temp[i] = array[i]; return temp; } // Resizes the array of Edges. Can make it larger or smaller, depending // on what newSize is. protected StringLinkList[] resize (StringLinkList[] array, int newSize) { StringLinkList[] temp = new StringLinkList[newSize]; int smallerSize = newSize; if(array.length < smallerSize) smallerSize = array.length; for(int i = 0; i < smallerSize; i++) temp[i] = array[i]; for (int i = smallerSize ; i < temp.length ; i ++) temp [i] = new StringLinkList(); return temp; } // Adds a new vertex public void addVertex(String newVertex) { if(getIndex(newVertex) != -1) { System.out.print("addVertex: "); System.out.print(newVertex); System.out.println(" failed -- vertex already exists."); return; } // if array of vertices is full, we need to expand it and // also expand Edges if (names.length == numVertices) { names = resize(names, 2*numVertices+1); Edges = resize(Edges, 2*numVertices+1); } names[numVertices++] = newVertex; } // Adds a new edge public void addEdge(String vertex1, String vertex2) { int i = getIndex(vertex1); if(i == -1) { System.out.print("addEdge failed: "); System.out.print(vertex1); System.out.println(" does not exist."); return; } int j = getIndex(vertex2); if(j == -1) { System.out.print("addEdge failed: "); System.out.print(vertex2); System.out.println(" does not exist."); return; } Edges[i].insertFirst(names[j]); Edges[j].insertFirst(names[i]); numEdges++; } // Prints the neighbors of the given vertex public void printAdjacencyList (String vertex) { int i = getIndex(vertex); if(i == -1) { System.out.print("addEdge failed: "); System.out.print(vertex); System.out.println(" does not exist."); return; } System.out.print (vertex + " is connected to "); Edges [i].displayList(); } // returns the names of all the neighbors of a given vertex in a // String array public String[] getNeighbors(String vertex) { int source = getIndex(vertex); if(source == -1) { System.out.print("getNeighbors failed: Vertex "); System.out.print(vertex); System.out.println(" does not exist."); return null; } return Edges[source].copyIntoArray(); } // returns the indices of all the neighbors of a given vertex. The // vertex is specified as an index and the neighbors are returned // in an int array public int[] getNeighbors(int index) { if((index < 0) || (index >= numVertices)) { System.out.print("getNeighbors failed: Index"); System.out.print(index); System.out.println(" is out of bounds."); return null; } // Call the earlier getNeighbors function to get the names of // neighbors String[] nbrNames = getNeighbors(names[index]); // Turn the array of neighbor names into an array // of neighbor indices int[] nbrIndices = new int[nbrNames.length]; for(int i = 0; i < nbrIndices.length; i++) nbrIndices[i] = getIndex(nbrNames[i]); return nbrIndices; } // returns the degree of a vertex with given name public int degree(String vertex) { // Get the index of the vertex int i = getIndex(vertex); if(i == -1) { System.out.print("In degree: "); System.out.print(vertex); System.out.println(" does not exist."); return -1; } // Call the other degree function that returns the degree // of a vertex, given its index return degree(i); } // returns the degree of a vertex with given index public int degree(int index) { return Edges[index].size(); } // Boolean method that tells us if {v1, v2} is an edge in the graph public boolean isEdge(String vertex1, String vertex2) { int i = getIndex(vertex1); if(i == -1) { System.out.print("isEdge failed: "); System.out.print(vertex1); System.out.println(" does not exist."); return false; } int j = getIndex(vertex2); if(j == -1) { System.out.print("isEdge failed: "); System.out.print(vertex2); System.out.println(" does not exist."); return false; } // if vertex2 exists in the adjacency list of // vertex1, then return true return (Edges[i].find(vertex2) != null); } // Computes the clustering coefficient of a vertex. This is the // version that takes a vertex index as argument. public double clusteringCoefficient(int i) { // Copy the adjacency list into an array, for easy access // copyIntoArray is a new method in the GenericLinkList class String[] temp = Edges[i].copyIntoArray(); // initialize edges-in-neighborhood to 0 int edgesInNbd = 0; // Scan pairs of neighbors and increment counter whenever // there is an edge for(int j = 0; j < temp.length; j++) for(int k = 0; k < j; k++) if(isEdge(temp[j], temp[k])) edgesInNbd++; // if there are no neighbors or one neighbor then, clustering // coefficient is trivially defined to be 1. Otherwise, // compute the ratio of number of edges in neighborhood to // the total number of pairs of neighbors if(temp.length <= 1) return 1; else return edgesInNbd*2.0/(temp.length*(temp.length-1)); } // Computes the clustering coefficient of a vertex. This is the // version that takes a vertex name as argument. public double clusteringCoefficient(String v) { int i = getIndex(v); if(i == -1) { System.out.print("clusteringCoefficient failed: "); System.out.print(v); System.out.println(" does not exist."); return 0; } return clusteringCoefficient(i); } // Computes the clustering coefficient of the entire graph // added on 2/23 public double clusteringCoefficient() { double sum = 0; for(int i = 0; i < numVertices; i++) sum = sum + clusteringCoefficient(i); return sum/numVertices; } // Checks whether the graph is connected or not by calling breadthFirstSearch public boolean isConnected() { // Perform breadth first search int[] bfsTree = breadthFirstSearch(names[0]); // Scan the bfsTree array, looking for -1. The graph // is not connected if there is -1 in this array for(int i = 0; i < bfsTree.length; i++) if(bfsTree[i] == -1) return false; return true; } // Returns a 2-d array of vertex names representing the connected components // of the graph. Each row in the 2-d array is a connected component. public String[][] connectedComponents() { // The maximum number of connected components equals the number // of vertices; so start by allocating this many rows. String[][] cc = new String[numVertices][]; // Keeps track of which vertices have been visited by repeated // calls to bfs boolean[] visited = new boolean[numVertices]; for(int i = 0; i < numVertices; i++) visited[i] = false; // Keeps track of the number of vertices have been visited by repeated // calls to bfs int numVisited = 0; // Keeps track of the number of connected components int numComponents = 0; // Repeat bfs until all vertices have been visited while(numVisited < numVertices) { // Scan visited until an unvisited vertex is found // and start bfs at that source int source; for(source = 0; source < numVisited; source++) if(!visited[source]) break; // Compute the bfsTree starting at this source int[] bfsTree = breadthFirstSearch(names[source]); // Scan bfsTree to count number of newly visited vertices int countNewVisited = 0; for(int i = 0; i < numVertices; i++) if(bfsTree[i] != -1) countNewVisited++; // Allocate a row of size countNewVisited in the current row of // cc to store the new connected component cc[numComponents] = new String[countNewVisited]; // Scan bfsTree again, this time to copy the newly visited // vertices into cc and set them as visited countNewVisited = 0; for(int i = 0; i < numVertices; i++) if(bfsTree[i] != -1) { cc[numComponents][countNewVisited++] = names[i]; visited[i] = true; } // Update numVisited numVisited = numVisited + countNewVisited; // Update numComponents numComponents++; } // end while // Resize cc to have exactly as mamy rows as numComponents String[][] newCC = new String[numComponents][]; for(int i = 0; i < numComponents; i++) newCC[i] = cc[i]; return newCC; } // Computes a shortest path (in hops) from source to destination. Does // this by simply calling breadthFirstSearch and following the parent // pointers in the BFS tree. If the source and destination are not in // the same component, returns null. Otherwise, returns a String array // of vertices in a shortest path public String[] shortestPath(String source, String dest) { // Get index of source int sourceIndex = getIndex(source); if(sourceIndex == -1) { System.out.print("breadthFirstSearch failed: "); System.out.print(source); System.out.println(" does not exist."); return null; } // Get index of destination int destIndex = getIndex(dest); if(destIndex == -1) { System.out.print("breadthFirstSearch failed: "); System.out.print(dest); System.out.println(" does not exist."); return null; } // Perform a BFS from destination int[] bfsTree = breadthFirstSearch(destIndex); // If source is unreachable from destination if(bfsTree[sourceIndex] == -1) return null; // Define a String[] for shortest path and place the source vertex // in it String[] path = new String[numVertices]; path[0] = names[sourceIndex]; // Start following parent pointers and store each new vertex // encountered, in the path array. The while-loop executes // until the root of the BFS tree is encountered int currentIndex = sourceIndex; int pathLength = 0; while(currentIndex != bfsTree[currentIndex]) { currentIndex = bfsTree[currentIndex]; pathLength++; path[pathLength] = names[currentIndex]; } // Resize the path array to be exactly of the correct size String[] newPath = new String[pathLength + 1]; for(int i = 0; i < newPath.length; i++) newPath[i] = path[i]; return newPath; } // Breadth first search function that takes a vertex name as argument; // returns a breadth first search tree // stored in an array of integers with the entry in slot i containing // the index of the parent of the vertex with index i // parent of source is itself; unvisited nodes have parent -1 public int[] breadthFirstSearch(String source) { int sourceIndex = getIndex(source); if(sourceIndex == -1) { System.out.print("breadthFirstSearch failed: "); System.out.print(source); System.out.println(" does not exist."); return null; } return breadthFirstSearch(sourceIndex); } // Breadth first search function that takes a vertex index as argument; // returns a breadth first search tree // stored in an array of integers with the entry in slot i containing // the index of the parent of the vertex with index i // parent of source is itself; unvisited nodes have parent -1 public int[] breadthFirstSearch(int sourceIndex) { // Initialize the bfsTree array; the entry -1 means // not yet visited. int[] bfsTree = new int[numVertices]; for(int i = 0; i < numVertices; i++) bfsTree[i] = -1; // The parent of the tree root is itself bfsTree[sourceIndex] = sourceIndex; // Then initialize the visited array boolean[] visited = new boolean[numVertices]; for(int i = 0; i < numVertices; i++) visited[i] = false; visited[sourceIndex] = true; // Then initialize the queue Queue Q = new Queue(numVertices); Q.enqueue(sourceIndex); while(!Q.isEmpty()) { // get the index of the vertex first in line int current = Q.dequeue(); // Get the indices of the neighbors of the current vertex int[] neighbors = getNeighbors(current); // Scan the neighbors for(int i = 0; i < neighbors.length; i++) { // Get the index of the current neighbor int currentNeighbor = neighbors[i]; // Check if the neighbor is new, i.e., not visited // If so, mark the neighbor as visited, enqueue the neighbor, and // set the neighbor's parent in bfsTree if(!visited[currentNeighbor]) { visited[currentNeighbor] = true; Q.enqueue(currentNeighbor); bfsTree[currentNeighbor] = current; } } // end-scanning neighbors } // end-while Q is not empty return bfsTree; } } // end of class