net.jangaroo.jooc.util.GraphUtil Maven / Gradle / Ivy
package net.jangaroo.jooc.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A utility for finding the strongly connected components of a graph,
* implementing Kosaraju's algorithm non-recursively.
*/
public class GraphUtil {
public static Collection> stronglyConnectedComponent(Map> graph) {
Map> reversed = reverse(graph);
Map> sccsByRoot = new HashMap>();
Set assigned = new HashSet();
for (T root : kosarajuSort(graph)) {
Deque todo = new LinkedList();
todo.add(root);
while (!todo.isEmpty()) {
T current = todo.removeLast();
if (assigned.add(current)) {
Set component = getOrAdd(sccsByRoot, root);
component.add(current);
todo.addAll(reversed.get(current));
}
}
}
return sccsByRoot.values();
}
static Map> reverse(Map> graph) {
Map> result = new HashMap>();
for (Map.Entry> entry : graph.entrySet()) {
T predecessor = entry.getKey();
getOrAdd(result, predecessor);
for (T successor : entry.getValue()) {
Set predecessors = getOrAdd(result, successor);
predecessors.add(predecessor);
}
}
return result;
}
private static Set getOrAdd(Map> map, T key) {
Set predecessors = map.get(key);
if (predecessors == null) {
predecessors = new HashSet();
map.put(key, predecessors);
}
return predecessors;
}
static Deque kosarajuSort(Map> graph) {
Set reached = new HashSet();
Set processed = new HashSet();
Deque sorted = new LinkedList();
Deque todo = new LinkedList(graph.keySet());
while (!todo.isEmpty()) {
T current = todo.getLast();
if (reached.add(current)) {
// New node to be processed.
Collection successors = graph.get(current);
if (successors != null) {
for (T successor : successors) {
// Make sure to add only successors not already reached,
// so that membership in the set reached is an indicator of a
// backtracking step after all successors have been processed.
if (!reached.contains(successor)) {
todo.add(successor);
}
}
}
} else {
// All successors have been processed.
todo.removeLast();
if (processed.add(current)) {
// The node
sorted.addFirst(current);
}
}
}
return sorted;
}
/**
* Find the shortest path from start to end.
* @param graph
* @param start
* @param end
* @param
* @return
*/
public static List findPath(Map> graph, T start, T end) {
// Breadth-first search for the shortest path.
Map predecessors = new HashMap();
List todo = new ArrayList();
todo.add(start);
while (!todo.isEmpty()) {
List nextTodo = new ArrayList();
for (T current : todo) {
Collection successors = graph.get(current);
if (successors != null) {
for (T successor : successors) {
if (!predecessors.containsKey(successor)) {
predecessors.put(successor, current);
nextTodo.add(successor);
}
}
}
}
todo = nextTodo;
}
List result = new ArrayList();
result.add(end);
T current = end;
do {
current = predecessors.get(current);
if (current == null) {
return null;
}
result.add(current);
} while (!current.equals(start));
Collections.reverse(result);
return result;
}
}