(Graph<N> graph)
| 60 | } |
| 61 | |
| 62 | private void compute(Graph<N> graph) { |
| 63 | // use iterative (non-recursive) algorithm to avoid stack overflow |
| 64 | // for large graph |
| 65 | int index = 0; |
| 66 | Map<N, Integer> indexes = Maps.newMap(graph.getNumberOfNodes()); |
| 67 | Map<N, Integer> lows = Maps.newMap(graph.getNumberOfNodes()); |
| 68 | Deque<N> stack = new ArrayDeque<>(); |
| 69 | Set<N> inStack = Sets.newSet(); |
| 70 | for (N curr : graph) { |
| 71 | if (indexes.containsKey(curr)) { |
| 72 | continue; |
| 73 | } |
| 74 | Deque<N> workStack = new ArrayDeque<>(); |
| 75 | workStack.push(curr); |
| 76 | while (!workStack.isEmpty()) { |
| 77 | N node = workStack.peek(); |
| 78 | if (!indexes.containsKey(node)) { |
| 79 | indexes.put(node, index); |
| 80 | lows.put(node, index); |
| 81 | ++index; |
| 82 | stack.push(node); |
| 83 | inStack.add(node); |
| 84 | } |
| 85 | boolean hasUnvisitedSucc = false; |
| 86 | for (N succ : graph.getSuccsOf(node)) { |
| 87 | if (!indexes.containsKey(succ)) { |
| 88 | workStack.push(succ); |
| 89 | hasUnvisitedSucc = true; |
| 90 | break; |
| 91 | } else if (indexes.get(node) < indexes.get(succ)) { |
| 92 | // node->succ is a forward edge |
| 93 | lows.put(node, Math.min(lows.get(node), lows.get(succ))); |
| 94 | } else if (inStack.contains(succ)) { |
| 95 | lows.put(node, Math.min(lows.get(node), indexes.get(succ))); |
| 96 | } |
| 97 | } |
| 98 | if (!hasUnvisitedSucc) { |
| 99 | if (lows.get(node).equals(indexes.get(node))) { |
| 100 | collectSCC(node, stack, inStack, graph); |
| 101 | } |
| 102 | workStack.pop(); |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | private void collectSCC(N node, Deque<N> stack, Set<N> inStack, Graph<N> graph) { |
| 109 | List<N> scc = new ArrayList<>(); |
no test coverage detected