package com.anuragkapur.ada1; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; /** * Compute SCCs * * @author anuragkapur */ public class StronglyConnectedComponents { private static int noOfNodes = 875714; private static Set<Integer> vertices = new HashSet<Integer>(); private static Map<Integer,List<Integer>> edges = new HashMap<Integer, List<Integer>>(); private static Map<Integer,List<Integer>> reverseEdges = new HashMap<Integer, List<Integer>>(); private static int finishTime = -1; private static int[] nodes = new int[noOfNodes + 1]; private static int[] nodesSortedByFinishTime = new int[noOfNodes]; private static int sccSize = 0; private static List<Integer> sccSizes = new ArrayList<Integer>(); /** * Computes the Graph DS consisting of Set of vertices and Map of egdes * * @param line */ private static void computeVerticesAndEdges(String line) { StringTokenizer tokenizer = new StringTokenizer(line," "); Integer vertex1 = new Integer(tokenizer.nextToken()); Integer vertex2 = new Integer(tokenizer.nextToken()); vertices.add(vertex1); vertices.add(vertex2); List<Integer> nodesOfEdge = null; // populate edges nodesOfEdge = edges.get(vertex1); if(nodesOfEdge != null) { nodesOfEdge.add(vertex2); }else { nodesOfEdge = new ArrayList<Integer>(); nodesOfEdge.add(vertex2); edges.put(vertex1, nodesOfEdge); } // populate reverse edges nodesOfEdge = null; nodesOfEdge = reverseEdges.get(vertex2); if(nodesOfEdge != null) { nodesOfEdge.add(vertex1); }else { nodesOfEdge = new ArrayList<Integer>(); nodesOfEdge.add(vertex1); reverseEdges.put(vertex2, nodesOfEdge); } } /** * DFS on G-reverse * * @param startNode */ private static void dfsGRev(Integer startNode) { // mark node i as explored nodes[startNode.intValue()] = 1; // find edges with start node as tail and another node as head in G-rev List<Integer> heads = reverseEdges.get(startNode); if(heads != null) { Iterator<Integer> headsIterator = heads.iterator(); while(headsIterator.hasNext()) { Integer head = (Integer)headsIterator.next(); // ignore self loops if(head.intValue() == startNode.intValue()) { continue; } if(nodes[head.intValue()] == 0) { // node not already explored dfsGRev(head); } } } finishTime ++; nodesSortedByFinishTime[finishTime] = startNode.intValue(); } /** * Compute finishing times on G-Rev */ private static void computeFinishingTimesOnGRev() { // iterate over all vertices and compute DFS on G-rev for(int i=0; i<noOfNodes; i++) { if(nodes[i+1] == 0) { // node is unexplored dfsGRev(new Integer(i+1)); } } } /** * DFS on G * * @param startNode */ private static void dfs(Integer startNode) { // increment SCC size sccSize ++; // mark node i as explored nodes[startNode.intValue()] = 1; // find edges with start node as tail and another node as head in G List<Integer> heads = edges.get(startNode); if(heads != null) { Iterator<Integer> headsIterator = heads.iterator(); while(headsIterator.hasNext()) { Integer head = (Integer)headsIterator.next(); // ignore self loops if(head.intValue() == startNode.intValue()) { continue; } if(nodes[head.intValue()] == 0) { // node not already explored dfs(head); } } } } /** * Compute Strongly connected components on G */ private static void computeSCCs() { // reset all nodes to unexplored for (int i = 0; i < nodes.length; i++) { nodes[i] = 0; } // iterate over all vertices and compute DFS for (int i = nodesSortedByFinishTime.length-1; i >= 0; i--) { if(nodes[nodesSortedByFinishTime[i]] == 0) { // node is unexplored sccSize = 0; dfs(new Integer(nodesSortedByFinishTime[i])); //System.out.println("sccSize :: " + sccSize); sccSizes.add(new Integer(sccSize)); } } int sccs[] = new int[sccSizes.size()]; for (int i = 0; i < sccs.length; i++) { sccs[i] = sccSizes.get(i).intValue(); } // sort them Arrays.sort(sccs); for (int i = 0; i < 5; i++) { if(sccs.length - 1 - i >= 0) { System.out.println(sccs[sccs.length - 1 - i]); } } } /** * @param args */ public static void main(String[] args) { File file = new File("src/com/anuragkapur/ada1/scc.txt"); String line = null; BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); while((line = br.readLine()) != null) { computeVerticesAndEdges(line); } System.out.println("Input parsing and graph construction complete"); computeFinishingTimesOnGRev(); System.out.println("DFS round 1 complete"); computeSCCs(); System.out.println("DFS round 2 complete"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if(br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } } }